diff --git a/data/Vortex86MX-eng/Makefile b/data/Vortex86MX-eng/Makefile
index 03f143df0a..55e69c70fc 100644
--- a/data/Vortex86MX-eng/Makefile
+++ b/data/Vortex86MX-eng/Makefile
@@ -381,7 +381,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
-docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
+docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(KERNEL)/docs/sysfuncs.txt
cp $(KERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT
diff --git a/data/Vortex86MX-eng/Makefile.fasm b/data/Vortex86MX-eng/Makefile.fasm
index 26e456d6e3..67f4b4debe 100644
--- a/data/Vortex86MX-eng/Makefile.fasm
+++ b/data/Vortex86MX-eng/Makefile.fasm
@@ -28,25 +28,32 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 $$< "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
+define fasm_nokpack_meta_rule
+$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
+-include .deps/$(4).Po
+endef
+
progname=$(call respace,$(basename $(notdir $(call binarypart,$(f)))))
binarydir=$(subst ./,,$(dir $(call binarypart,$(f))))
-$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
+$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD) $(SKIN_SOURCES),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
+$(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
- fasm -m 65536 $< "$@" -s .deps/kernel.fas
- prepsrc .deps/kernel.fas /dev/null
- prepsrc .deps/kernel.fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
- kerpack $@
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$<" "$@" -s $$tmpfile && \
+ fasmdep -e $$tmpfile > .deps/kernel.Po && \
+ rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po
diff --git a/data/common/fasmdep/fas.inc b/data/common/fasmdep/fas.inc
new file mode 100644
index 0000000000..0199b639f8
--- /dev/null
+++ b/data/common/fasmdep/fas.inc
@@ -0,0 +1,90 @@
+; Structures describing fasm symbol files.
+define FAS_SIGNATURE 1A736166h
+virtual at 0
+fas_header:
+.signature dd ? ; Signature 1A736166h.
+.major db ? ; Major version of flat assembler.
+.minor db ? ; Minor version of flat assembler.
+.headersize dw ? ; Length of header.
+.input dd ? ; Offset of input file name in the strings table.
+.output dd ? ; Offset of output file name in the strings table.
+.strings_offs dd ? ; Offset of strings table.
+.strings_size dd ? ; Length of strings table.
+.symbols_offs dd ? ; Offset of symbols table.
+.symbols_size dd ? ; Length of symbols table.
+.preproc_offs dd ? ; Offset of preprocessed source.
+.preproc_size dd ? ; Length of preprocessed source.
+.asm_offs dd ? ; Offset of assembly dump.
+.asm_size dd ? ; Length of assembly dump.
+.section_offs dd ? ; Offset of section names table.
+.section_size dd ? ; Length of section names table.
+.symref_offs dd ? ; Offset of symbol references dump.
+.symref_size dd ? ; Length of symbol references dump.
+.size:
+end virtual
+
+virtual at 0
+preproc_line_header:
+; When the line was loaded from source, this field contains
+; either zero (if it is the line from the main input file), or
+; an offset inside the preprocessed source to the name of file,
+; from which this line was loaded (the name of file is zero-ended
+; string).
+; When the line was generated by macroinstruction, this field
+; contains offset inside the preprocessed source to the
+; pascal-style string specifying the name of macroinstruction,
+; which generated this line.
+.source_name dd ?
+; Bits 0-30 contain the number of this line.
+; If the highest bit is zeroed, this line was loaded from source.
+; If the highest bit is set, this line was generated by
+; macroinstruction.
+.line_number dd ?
+; If the line was loaded from source, this field contains
+; the position of the line inside the source file, from which it
+; was loaded.
+; If line was generated by macroinstruction, this field contains
+; the offset of preprocessed line, which invoked the
+; macroinstruction. If line was generated by instantaneous macro,
+; this field is equal to the next one.
+.line_offset dd ?
+; If the line was generated by macroinstruction, this field
+; contains offset of the preprocessed line inside the definition
+; of macro, from which this one was generated.
+.line_macro_offs dd ?
+; The tokenized contents of line.
+.contents:
+end virtual
+
+virtual at 0
+asm_row:
+.out_offs dd ? ; Offset in output file.
+.preproc_offs dd ? ; Offset of line in preprocessed source.
+.this dq ? ; Value of $ address.
+.extended_sib dd ? ; Extended SIB for the $ address, the first two bytes
+ ; are register codes and the second two bytes are
+ ; corresponding scales.
+; If the $ address is relocatable, this field contains
+; information about section or external symbol, to which
+; it is relative - otherwise this field is zero.
+; When the highest bit is cleared, the address is
+; relative to a section, and the bits 0-30 contain
+; the index (starting from 1) in the table of sections.
+; When the highest bit is set, the address is relative
+; to an external symbol, and the bits 0-30 contain the
+; the offset of the name of this symbol in the strings
+; table.
+.this_base dd ?
+.this_type db ? ; Type of $ address value (as in table 2.2).
+.code_type db ? ; Type of code - possible values are 16, 32, and 64.
+; If the bit 0 is set, then at this point the assembly
+; was taking place inside the virtual block, and the
+; offset in output file has no meaning here.
+; If the bit 1 is set, the line was assembled at the
+; point, which was not included in the output file for
+; some other reasons (like inside the reserved data at
+; the end of section).
+.flags db ?
+.this_high db ? ; The higher bits of value of $ address.
+.sizeof:
+end virtual
diff --git a/data/common/fasmdep/fasmdep.asm b/data/common/fasmdep/fasmdep.asm
new file mode 100644
index 0000000000..7a22db0446
--- /dev/null
+++ b/data/common/fasmdep/fasmdep.asm
@@ -0,0 +1,765 @@
+; This program parses .fas file generated by fasm
+; and prints the list of dependencies for make.
+; Usage: fasmdep [-e] [ []].
+; If input file is not given, the program reads from stdin.
+; If output file is not given, the program writes to stdout.
+; If the option -e is given, the program also creates an empty
+; goal for every dependency, this prevents make errors when
+; files are renamed.
+
+; This definition controls the choice of platform-specific code.
+;define OS WINDOWS
+define OS LINUX
+
+; Program header.
+match =WINDOWS,OS { include 'windows_header.inc' }
+match =LINUX,OS { include 'linux_header.inc' }
+
+include 'fas.inc'
+
+; Main code
+start:
+; 1. Setup stack frame, zero-initialize all local variables.
+virtual at ebp - .localsize
+.begin:
+.flags dd ? ; 1 if '-e' is given
+.in dd ? ; handle of input file
+.out dd ? ; handle of output file
+.buf dd ? ; pointer to data of .fas file
+.allocated dd ? ; number of bytes allocated for .buf
+.free dd ? ; number of bytes free in .buf
+.outstart dd ? ; offset in .buf for start of data to output
+.names dd ? ; offset in .buf for start of output names
+.include dd ? ; offset in .buf to value of %INCLUDE%
+.testname dd ? ; offset in .buf for start of current file name
+.prevfile dd ? ; offset in .buf for previous included file name
+.prevfilefrom dd ? ; offset in .buf for .asm/.inc for .prevfile
+.localsize = $ - .begin
+match =LINUX,OS {
+.argc dd ?
+.argv:
+}
+end virtual
+
+ mov ebp, esp
+ xor eax, eax
+repeat .localsize / 4
+ push eax
+end repeat
+; 2. Call the parser of the command line.
+ call get_params
+; 3. Load the input file.
+; Note that stdin can be a pipe,
+; useful in bash-construction "fasm input.asm -s >(fasmdep > input.Po)",
+; so its size is not always known in the beginning.
+; So, we must read input file in portions, reallocating memory if needed.
+.readloop:
+; 3a. If the size is less than 32768 bytes, reallocate buffer.
+; Otherwise, goto 3c.
+ cmp [.free], 32768
+ jae .norealloc
+; 3b. Reallocate buffer.
+; Start with 65536 bytes and then double the size every time.
+ mov eax, 65536
+ mov ecx, [.allocated]
+ add ecx, ecx
+ jz @f
+ mov eax, ecx
+@@:
+ call realloc
+.norealloc:
+; 3c. Read the next portion.
+ call read
+ sub [.free], eax
+ test eax, eax
+ jnz .readloop
+; 4. Sanity checks.
+; We don't use .section_* and .symref_*, so allow them to be absent.
+ mov edi, [.allocated]
+ sub edi, [.free]
+; Note that edi = number of bytes which were read
+; and simultaneously pointer to free space in the buffer relative to [.buf].
+ cmp edi, fas_header.section_offs
+ jb badfile
+ mov ebx, [.buf]
+ cmp [ebx+fas_header.signature], FAS_SIGNATURE
+ jnz badfile
+ cmp [ebx+fas_header.headersize], fas_header.section_offs
+ jb badfile
+; 5. Get %INCLUDE% environment variable, it will be useful.
+ mov [.include], edi
+ mov esi, include_variable
+ sub esi, ebx
+ call get_environment_variable
+; 6. Start writing dependencies: copy output and input files.
+ mov [.outstart], edi
+; 6a. Copy output name.
+ mov esi, [ebx+fas_header.output]
+ call copy_asciiz_escaped
+; 6b. Write ": ".
+ stdcall alloc_in_buf, 2
+ mov word [edi+ebx], ': '
+ inc edi
+ inc edi
+; 6c. Copy input name.
+ mov [.names], edi
+ mov esi, [ebx+fas_header.input]
+ call copy_asciiz_escaped
+; 7. Scan for 'include' dependencies.
+; 7a. Get range for scanning.
+ mov edx, [ebx+fas_header.preproc_size]
+ mov esi, [ebx+fas_header.preproc_offs]
+ add edx, esi
+.include_loop:
+; 7b. Loop over preprocessed lines in the range.
+ cmp esi, edx
+ jae .include_done
+; 7c. For every line, ignore header and do internal loop over tokens.
+ add esi, preproc_line_header.contents
+.include_loop_int:
+; There are five types of tokens:
+; 1) "start preprocessor data" with code ';'
+; 2) "quoted string" with code '"'
+; 3) "word" with code 1Ah
+; 4) one-byte tokens like "+", "(" and so on
+; 5) "end-of-line" token with code 0.
+ mov al, [esi+ebx]
+ inc esi
+; 7d. First, check token type parsing the first byte.
+; For preprocessor tokens, continue to 7e.
+; For quoted strings, go to 7g.
+; For words, go to 7h.
+; For end-of-line, exit the internal loop, continue the external loop.
+; Otherwise, just continue the internal loop.
+ cmp al, ';'
+ jnz .notprep
+; 7e. For "include" tokens length=7, token is "include", the next token is
+; quoted string.
+; These tokens are handled in 7f, other preprocessor tokens in 7g.
+ cmp byte [esi+ebx], 7
+ jnz .notinclude
+ cmp dword [esi+ebx+1], 'incl'
+ jnz .notinclude
+ cmp dword [esi+ebx+5], 'ude"'
+ jnz .notinclude
+; 7f. Skip over end of this token and the type byte of the next token;
+; this gives the pointer to length-prefixed string, which should be added
+; to dependencies. Note that doing that skips that string,
+; so after copying just continue the internal loop.
+ add esi, 9
+ call add_separator
+ call copy_name_escaped
+ jmp .include_loop_int
+.quoted:
+; 7g. To skip a word, load the dword length and add it to pointer.
+ add esi, [esi+ebx]
+ add esi, 4
+ jmp .include_loop_int
+.notinclude:
+; 7h. To skip this token, load the byte length and add it to pointer.
+; Note that word tokens and preprocessor tokens have a similar structure,
+; so both are handled here.
+ movzx eax, byte [esi+ebx]
+ lea esi, [esi+eax+1]
+ jmp .include_loop_int
+.notprep:
+ cmp al, 1Ah
+ jz .notinclude
+ cmp al, '"'
+ jz .quoted
+ test al, al
+ jnz .include_loop_int
+ jmp .include_loop
+.include_done:
+; 8. Scan for 'file' dependencies.
+; 8a. Get range for scanning.
+; Note that fas_header.asm_size can be slighly greater than the
+; real size, so we break from the loop when there is no space left
+; for the entire asm_row structure, not when .asm_size is exactly reached.
+ mov esi, [ebx+fas_header.asm_offs]
+ mov edx, [ebx+fas_header.asm_size]
+ add edx, esi
+ sub edx, asm_row.sizeof
+ push edx
+ jc .file_done
+.file_loop:
+; 8b. Loop over assembled lines in the range.
+ cmp esi, [esp]
+ ja .file_done
+; 8c. For every assembled line, look at first token;
+; go to 8d for token 'file',
+; go to 8e for token 'format',
+; go to 8f for token 'section',
+; continue the loop otherwise.
+ mov eax, [esi+ebx+asm_row.preproc_offs]
+ add eax, [ebx+fas_header.preproc_offs]
+ cmp byte [eax+ebx+preproc_line_header.contents], 1Ah
+ jnz .file_next
+ movzx ecx, byte [eax+ebx+preproc_line_header.contents+1]
+ cmp cl, 4
+ jnz .file_no_file
+ cmp dword [eax+ebx+preproc_line_header.contents+2], 'file'
+ jnz .file_no_file4
+; 8d. For lines starting with 'file' token, loop over tokens and get names.
+; Note that there can be several names in one line.
+; Parsing of tokens is similar to step 5 with the difference that
+; preprocessor token stops processing: 'file' directives are processed
+; in assembler stage.
+; We save/restore esi since it is used for another thing in the internal loop;
+; we push eax, which currently contains ebx-relative pointer to
+; preproc_line_header, to be able to access it from resolve_name.
+ push esi eax
+ lea esi, [eax+preproc_line_header.contents+6]
+.file_loop_int:
+ mov al, [esi+ebx]
+ inc esi
+ test al, al
+ jz .file_loop_int_done
+ cmp al, ';'
+ jz .file_loop_int_done
+ cmp al, 1Ah
+ jz .fileword
+ cmp al, '"'
+ jnz .file_loop_int
+ call resolve_name
+ add esi, [esi+ebx-4]
+ jmp .file_loop_int
+.fileword:
+ movzx eax, byte [esi+ebx]
+ lea esi, [esi+eax+1]
+ jmp .file_loop_int
+.file_loop_int_done:
+ pop eax esi
+ jmp .file_next
+.file_no_file4:
+ cmp dword [eax+ebx+preproc_line_header.contents+2], 'data'
+ jnz .file_next
+ jmp .file_scan_from
+.file_no_file:
+ cmp cl, 6
+ jnz .file_no_format
+ cmp dword [eax+ebx+preproc_line_header.contents+2], 'form'
+ jnz .file_no_format
+ cmp word [eax+ebx+preproc_line_header.contents+6], 'at'
+ jnz .file_no_format
+; 8e. For lines starting with 'format' token, loop over tokens and look for stub name.
+; Note that there can be another quoted string, namely file extension;
+; stub name always follows the token 'on'.
+ mov edx, TokenOn
+ call scan_after_word
+ jmp .file_next
+.file_no_format:
+ cmp cl, 7
+ jnz .file_no_section
+ cmp dword [eax+ebx+preproc_line_header.contents+2], 'sect'
+ jnz .file_no_section
+ mov edx, dword [eax+ebx+preproc_line_header.contents+6]
+ and edx, 0FFFFFFh
+ cmp edx, 'ion'
+ jnz .file_no_section
+.file_scan_from:
+ mov edx, TokenFrom
+ call scan_after_word
+.file_no_section:
+.file_next:
+ add esi, asm_row.sizeof
+ jmp .file_loop
+.file_done:
+ pop edx
+; 9. Write result.
+; 9a. Append two newlines to the end of buffer.
+ stdcall alloc_in_buf, 2
+ mov word [edi+ebx], 10 * 101h
+ inc edi
+ inc edi
+; 9b. If '-e' option was given, duplicate dependencies list as fake goals
+; = copy all of them and append ":\n\n".
+ cmp [.flags], 0
+ jz .nodup
+ lea ecx, [edi+1]
+ mov esi, [.names]
+ sub ecx, esi
+ stdcall alloc_in_buf, ecx
+ add esi, ebx
+ add edi, ebx
+ rep movsb
+ mov byte [edi-3], ':'
+ mov word [edi-2], 10 * 101h
+ sub edi, ebx
+.nodup:
+; 9c. Write to output file.
+ mov eax, [.outstart]
+ sub edi, eax
+ add eax, ebx
+ call write
+; 10. Exit.
+ xor eax, eax
+ call exit
+
+; Helper procedure for steps 8e and 8f of main algorithm.
+; Looks for quoted strings after given word in edx.
+scan_after_word:
+ push esi eax
+ lea esi, [eax+preproc_line_header.contents+2+ecx]
+.loop:
+ xor ecx, ecx
+.loop_afterword:
+ mov al, [esi+ebx]
+ inc esi
+ test al, al
+ jz .loop_done
+ cmp al, ';'
+ jz .loop_done
+ cmp al, 1Ah
+ jz .word
+ cmp al, '"'
+ jnz .loop
+ test ecx, ecx
+ jz .skip_quoted
+ call resolve_name
+.loop_done:
+ pop eax esi
+ ret
+.skip_quoted:
+ add esi, [esi+ebx]
+ add esi, 4
+ jmp .loop
+.word:
+ movzx ecx, byte [esi+ebx]
+ lea esi, [esi+ecx+1]
+ cmp cl, byte [edx]
+ jnz .loop
+ push esi edi
+ add esi, ebx
+ sub esi, ecx
+ lea edi, [edx+1]
+ repz cmpsb
+ pop edi esi
+ jnz .loop
+ dec ecx
+ jmp .loop_afterword
+
+; Helper procedure for step 6 of the main procedure.
+; Copies the ASCIIZ name from strings section to the buffer.
+copy_asciiz_escaped:
+ add esi, [ebx+fas_header.strings_offs]
+.loop:
+ mov al, [esi+ebx]
+ test al, al
+ jz .nothing
+ call copy_char_escaped
+ jmp .loop
+.nothing:
+ ret
+
+; Helper procedure for step 7 of the main procedure.
+; Copies the name with known length to the buffer.
+copy_name_escaped:
+ mov ecx, [esi+ebx]
+ add esi, 4
+ test ecx, ecx
+ jz .nothing
+ push ecx
+.loop:
+ mov al, [esi+ebx]
+ call copy_char_escaped
+ dec dword [esp]
+ jnz .loop
+ pop ecx
+.nothing:
+ ret
+
+; Helper procedure for steps 7 and 8 of the main procedure.
+; Writes separator of file names in output = " \\\n".
+add_separator:
+ stdcall alloc_in_buf, 3
+ mov word [edi+ebx], ' \'
+ mov byte [edi+ebx+2], 10
+ add edi, 3
+ ret
+
+; Helper procedure for step 7 of the main procedure.
+; Resolves the path to 'file' dependency and copies
+; the full name to the buffer.
+resolve_name:
+; FASM uses the following order to search for referenced files:
+; * path of currently assembling file, which may be .asm or .inc
+; * paths from %INCLUDE% for versions >= 1.70
+; * current directory = file name is taken as is, without prepending dir name
+; We mirror this behaviour, trying to find an existing file somewhere.
+; There can be following reasons for the file can not be found anywhere:
+; * it has been deleted between compilation and our actions
+; * it didn't exist at all, compilation has failed
+; * we are running in environment different from fasm environment.
+; Assume that the last reason is most probable and that invalid dependency
+; is better than absent dependency (it is easier to fix an explicit error
+; than a silent one) and output file name without prepending dir name,
+; as in the last case. Actually, we even don't need to test existence
+; of the file in the current directory.
+ add esi, 4 ; skip string length
+; 1. Get ebx-relative pointer to preproc_line_header, see the comment in start.7d
+ mov eax, [esp+4]
+; 2. Get the path to currently processing file.
+ push esi
+.getpath:
+ test byte [eax+ebx+preproc_line_header.line_number+3], 80h
+ jz @f
+ mov eax, [eax+ebx+preproc_line_header.line_offset]
+ add eax, [ebx+fas_header.preproc_offs]
+ jmp .getpath
+@@:
+ mov edx, [eax+ebx+preproc_line_header.source_name]
+ test edx, edx
+ jz .frommain
+ add edx, [ebx+fas_header.preproc_offs]
+ jmp @f
+.frommain:
+ mov edx, [ebx+fas_header.input]
+ add edx, [ebx+fas_header.strings_offs]
+@@:
+; 3. Check that it is not a duplicate of the previous dependency.
+; 3a. Compare preprocessor units.
+ cmp edx, [start.prevfilefrom]
+ jnz .nodup
+; 3b. Compare string lengths.
+ mov eax, [start.prevfile]
+ mov ecx, [eax+ebx-4]
+ cmp ecx, [esi+ebx-4]
+ jnz .nodup
+; 3c. Compare string contents.
+ push esi edi
+ lea edi, [eax+ebx]
+ add esi, ebx
+ rep cmpsb
+ pop edi esi
+ jnz .nodup
+; 3d. It is duplicate, just return.
+ pop esi
+ ret
+.nodup:
+; 3e. It is not duplicate. Output separator.
+ mov [start.prevfilefrom], edx
+ mov [start.prevfile], esi
+ call add_separator
+; 4. Cut the last component of the path found in step 2.
+ mov ecx, edx
+ mov esi, edx
+.scanpath:
+ mov al, [edx+ebx]
+ test al, al
+ jz .scandone
+ cmp al, '/'
+ jz .slash
+ cmp al, '\'
+ jnz .scannext
+.slash:
+ lea ecx, [edx+1]
+.scannext:
+ inc edx
+ jmp .scanpath
+.scandone:
+ sub ecx, esi
+; 5. Try path found in step 4. If found, go to step 8.
+ mov [start.testname], edi
+ stdcall copy_string, esi, ecx
+ pop esi
+ call expand_environment
+ call test_file_exists
+ test eax, eax
+ jns .found
+ call revert_testname
+; 6. Try each of include paths. For every path, if file is found, go to step 8.
+; Otherwise, continue loop over include path.
+; Skip this step before 1.70.
+ cmp [ebx+fas_header.major], 1
+ ja .checkenv
+ jb .nocheckenv
+ cmp [ebx+fas_header.minor], 70
+ jb .nocheckenv
+.checkenv:
+ mov ecx, [start.include]
+.includeloop_ext:
+ mov eax, ecx
+.includeloop_int:
+ cmp byte [eax+ebx], 0
+ jz @f
+ cmp byte [eax+ebx], ';'
+ jz @f
+ inc eax
+ jmp .includeloop_int
+@@:
+ push eax
+ sub eax, ecx
+ jz @f
+ stdcall copy_string, ecx, eax
+ cmp byte [edi+ebx-1], '/'
+ jz .hasslash
+@@:
+ stdcall alloc_in_buf, 1
+ mov byte [edi+ebx], '/'
+ inc edi
+.hasslash:
+ call expand_environment
+ call test_file_exists
+ pop ecx
+ test eax, eax
+ jns .found
+ call revert_testname
+ cmp byte [ecx+ebx], 0
+ jz .notfound
+ inc ecx
+ cmp byte [ecx+ebx], 0
+ jnz .includeloop_ext
+.nocheckenv:
+.notfound:
+; 7. File not found neither near the current preprocessor unit nor in %INCLUDE%.
+; Assume that it is in the current directory.
+ call expand_environment
+.found:
+; 8. Currently we have file name from [start.testname] to edi;
+; it is zero-terminated and not space-escaped. Fix both issues.
+ dec edi
+ inc [start.free]
+ push esi
+ mov edx, [start.testname]
+.escapeloop:
+ cmp edx, edi
+ jae .escapedone
+ cmp byte [edx+ebx], ' '
+ jnz .noescape
+ stdcall alloc_in_buf, 1
+ mov ecx, edi
+ sub ecx, edx
+ push edi
+ add edi, ebx
+ lea esi, [edi-1]
+ std
+ rep movsb
+ cld
+ pop edi
+ inc edi
+ mov byte [edx+ebx], '\'
+ inc edx
+.noescape:
+ inc edx
+ jmp .escapeloop
+.escapedone:
+ pop esi
+ ret
+
+; Helper procedure for resolve_name.
+; Allocates space in the buffer and appends the given string to the buffer.
+copy_string:
+ mov eax, [esp+8]
+ test eax, eax
+ jz .nothing
+ stdcall alloc_in_buf, eax
+ mov ecx, [esp+4]
+.copy:
+ mov al, [ecx+ebx]
+ inc ecx
+ cmp al, '\'
+ jnz @f
+ mov al, '/'
+@@:
+ mov [edi+ebx], al
+ inc edi
+ dec dword [esp+8]
+ jnz .copy
+.nothing:
+ ret 8
+
+; Helper procedure for resolve_name. Undoes appending of last file name.
+revert_testname:
+ add [start.free], edi
+ mov edi, [start.testname]
+ sub [start.free], edi
+ ret
+
+; Helper procedure for resolve_name. Copies string from esi to edi,
+; expanding environment variables.
+expand_environment:
+; 1. Save esi to restore it in the end of function.
+ push esi
+; 2. Push string length to the stack to be used as a variable.
+ pushd [esi+ebx-4]
+; 3. Scan loop.
+.scan:
+; 3a. Scan for '%' sign.
+ call find_percent
+.justcopy:
+; 3b. Copy the part from the beginning of current portion to '%' sign,
+; advance pointer to '%' sign, or end-of-string if no '%' found.
+ push eax
+ sub eax, esi
+ stdcall copy_string, esi, eax
+ pop esi
+; 3c. If string has ended, break from the loop.
+ cmp dword [esp], 0
+ jz .scandone
+; 3d. Advance over '%' sign.
+ inc esi
+ dec dword [esp]
+; 3e. Find paired '%'.
+ call find_percent
+; 3f. If there is no paired '%', just return to 3b and copy remaining data,
+; including skipped '%'; after that, 3c would break from the loop.
+ dec esi
+ cmp dword [esp], 0
+ jz .justcopy
+; 3g. Otherwise, get the value of environment variable.
+; Since get_environment_variable requires zero-terminated string
+; and returns zero-terminated string, temporarily overwrite trailing '%'
+; and ignore last byte in returned string.
+; Also convert any backslashes to forward slashes.
+ inc esi
+ mov byte [eax+ebx], 0
+ push eax
+ push edi
+ call get_environment_variable
+ dec edi
+ inc [start.free]
+ pop eax
+.replaceslash:
+ cmp eax, edi
+ jz .replaceslash_done
+ cmp byte [eax+ebx], '\'
+ jnz @f
+ mov byte [eax+ebx], '/'
+@@:
+ inc eax
+ jmp .replaceslash
+.replaceslash_done:
+ pop esi
+ mov byte [esi+ebx], '%'
+; 3h. Advance over trailing '%'.
+ inc esi
+ dec dword [esp]
+; 3i. Continue the loop.
+ jmp .scan
+.scandone:
+; 4. Zero-terminate resulting string.
+ stdcall alloc_in_buf, 1
+ mov byte [edi+ebx], 0
+ inc edi
+; 5. Pop stack variable initialized in step 2.
+ pop eax
+; 6. Restore esi saved in step 1 and return.
+ pop esi
+ ret
+
+; Helper procedure for expand_environment.
+; Scans the string in esi with length [esp+4]
+; until '%' is found or line ended.
+find_percent:
+ mov eax, esi
+ cmp dword [esp+4], 0
+ jz .nothing
+.scan:
+ cmp byte [eax+ebx], '%'
+ jz .nothing
+ inc eax
+ dec dword [esp+4]
+ jnz .scan
+.nothing:
+ ret
+
+; Helper procedure for copy_{name,asciiz}_escaped.
+; Allocates space and writes one character, possibly escaped.
+copy_char_escaped:
+ cmp al, ' '
+ jnz .noescape
+ stdcall alloc_in_buf, 1
+ mov byte [edi+ebx], '\'
+ inc edi
+.noescape:
+ stdcall alloc_in_buf, 1
+ mov al, [esi+ebx]
+ inc esi
+ cmp al, '\'
+ jnz @f
+ mov al, '/'
+@@:
+ mov [edi+ebx], al
+ inc edi
+ ret
+
+; Helper procedure for ensuring that there is at least [esp+4]
+; free bytes in the buffer.
+alloc_in_buf:
+ mov eax, [esp+4]
+ sub [start.free], eax
+ jb .need_realloc
+ ret 4
+.need_realloc:
+ mov eax, [start.allocated]
+ add eax, eax
+ push ecx edx
+ call realloc
+ pop edx ecx
+ cmp [start.free], 0
+ jl .need_realloc
+ mov ebx, [start.buf]
+ ret 4
+
+badfile:
+ mov esi, badfile_string
+ call sayerr
+ mov al, 1
+ call exit
+
+information:
+ mov esi, information_string
+ call sayerr
+ mov al, 2
+ call exit
+
+nomemory:
+ mov esi, nomemory_string
+ call sayerr
+ mov al, 3
+ call exit
+
+in_openerr:
+ mov esi, in_openerr_string
+ jmp in_err
+readerr:
+ mov esi, readerr_string
+in_err:
+ call sayerr
+ mov al, 4
+ call exit
+
+out_openerr:
+ mov esi, out_openerr_string
+ jmp out_err
+writeerr:
+ mov esi, writeerr_string
+out_err:
+ call sayerr
+ mov al, 5
+ call exit
+
+; Platform-specific procedures.
+match =WINDOWS,OS { include 'windows_sys.inc' }
+match =LINUX,OS { include 'linux_sys.inc' }
+
+; Data
+macro string a, [b] {
+common
+ db a ## _end - a
+a db b
+a ## _end:
+}
+
+string information_string, 'Usage: fasmdep [-e] [ []]',10
+string badfile_string, 'Not .fas file',10
+string nomemory_string, 'No memory',10
+string in_openerr_string, 'Cannot open input file',10
+string readerr_string, 'Read error',10
+string out_openerr_string, 'Cannot create output file',10
+string writeerr_string, 'Write error',10
+
+include_variable db 'INCLUDE',0
+TokenOn db 2,'on'
+TokenFrom db 4,'from'
diff --git a/data/common/fasmdep/linux_header.inc b/data/common/fasmdep/linux_header.inc
new file mode 100644
index 0000000000..cbbddf4451
--- /dev/null
+++ b/data/common/fasmdep/linux_header.inc
@@ -0,0 +1,73 @@
+; Header for Linux program
+ format ELF executable 3
+ entry start
+; for system calls
+include 'unistd.inc'
+macro __mov a,b
+{
+if b eq
+else if ~(b eqtype 1)
+ mov a, b
+else if b = 0
+ xor a, a
+else if (b < 0x80) & (b >= -0x80)
+ push b
+ pop a
+else
+ mov a, b
+end if
+}
+macro kercall a,b,c,d,e,f,g
+{
+ __mov eax, a
+ __mov ebx, b
+ __mov ecx, c
+ __mov edx, d
+ __mov esi, e
+ __mov edi, f
+ __mov ebp, g
+ int 0x80
+}
+macro stdcall func,[arg]
+{
+reverse
+ pushd arg
+common
+ call func
+}
+PROT_READ = 0x1 ; page can be read
+PROT_WRITE = 0x2 ; page can be written
+PROT_EXEC = 0x4 ; page can be executed
+PROT_SEM = 0x8 ; page may be used for atomic ops
+PROT_NONE = 0x0 ; page can not be accessed
+PROT_GROWSDOWN = 0x01000000 ; mprotect flag: extend change to start of growsdown vma
+PROT_GROWSUP = 0x02000000 ; mprotect flag: extend change to end of growsup vma
+
+MAP_SHARED = 0x01 ; Share changes
+MAP_PRIVATE = 0x02 ; Changes are private
+MAP_TYPE = 0x0f ; Mask for type of mapping
+MAP_FIXED = 0x10 ; Interpret addr exactly
+MAP_ANONYMOUS = 0x20 ; don't use a file
+
+O_ACCMODE = 00000003
+O_RDONLY = 00000000
+O_WRONLY = 00000001
+O_RDWR = 00000002
+O_CREAT = 00000100 ; not fcntl
+O_EXCL = 00000200 ; not fcntl
+O_NOCTTY = 00000400 ; not fcntl
+O_TRUNC = 00001000 ; not fcntl
+O_APPEND = 00002000
+O_NONBLOCK = 00004000
+O_DSYNC = 00010000 ; used to be O_SYNC, see below
+FASYNC = 00020000 ; fcntl, for BSD compatibility
+O_DIRECT = 00040000 ; direct disk access hint
+O_LARGEFILE = 00100000
+O_DIRECTORY = 00200000 ; must be a directory
+O_NOFOLLOW = 00400000 ; don't follow links
+O_NOATIME = 01000000
+O_CLOEXEC = 02000000 ; set close_on_exec
+__O_SYNC = 04000000
+O_SYNC = (__O_SYNC + O_DSYNC)
+O_NDELAY = O_NONBLOCK
+segment readable executable
diff --git a/data/common/fasmdep/linux_sys.inc b/data/common/fasmdep/linux_sys.inc
new file mode 100644
index 0000000000..48aa8d00b1
--- /dev/null
+++ b/data/common/fasmdep/linux_sys.inc
@@ -0,0 +1,186 @@
+; Platform-specific procedures for Linux.
+
+; Reallocate memory at pointer [start.buf] and size [start.allocated],
+; new size is in eax.
+realloc:
+ push ebx esi edi
+ mov ecx, eax
+ push ebp
+ kercall __NR_mmap2, 0, , PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_ANONYMOUS, -1, 0
+ pop ebp
+ cmp eax, 0xFFFFF000
+ ja nomemory
+ add [start.free], ecx
+ xchg ecx, [start.allocated]
+ sub [start.free], ecx
+ mov edi, eax
+ xchg eax, [start.buf]
+ shr ecx, 2
+ jz .nothing
+ push ecx
+ mov esi, eax
+ rep movsd
+ pop ecx
+ shl ecx, 2
+ call free_eax_ecx
+.nothing:
+ pop edi esi ebx
+ ret
+
+; Read the next portion of input data to [start.buf].
+read:
+ mov ecx, [start.buf]
+ add ecx, [start.allocated]
+ mov edx, [start.free]
+ sub ecx, edx
+ kercall __NR_read, [start.in], ,
+ test eax, eax
+ js readerr
+ ret
+
+; Write output data: eax=pointer, edi=size.
+write:
+ mov ecx, eax
+ kercall __NR_write, [start.out], , edi
+ cmp eax, edi
+ jnz writeerr
+ ret
+
+; Parse command line, open input and output files.
+get_params:
+; 1. Initialize default streams: 0 for stdin, 1 for stdout.
+ inc [start.out]
+; 2. Prepare for scanning, skipping argv[0].
+ mov ebx, [start.argc]
+ cmp ebx, 1
+ jbe .noargs
+ dec ebx
+ lea esi, [start.argv+4] ; skip argv[0]
+ xor edi, edi ; no args parsed yet
+; 3. Parse loop.
+.parse:
+; 3a. Get the next argument.
+ lodsd
+; 3b. Check whether it is a known option.
+ cmp word [eax], '-e'
+ jnz @f
+ cmp byte [eax+2], 0
+ jnz @f
+; 3c. If it is, modify flags and continue the loop.
+ mov [start.flags], 1 ; '-e' is given
+ jmp .nextarg
+@@:
+; 3d. Otherwise, it is a name of input or output file.
+; edi keeps the count of names encountered before;
+; edi = 0 means this is input file, edi = 1 - output file,
+; otherwise this is third arg, which is an error
+ cmp edi, 1
+ ja information
+; 3e. Some parameters of __NR_open differ for input and output. Setup them.
+ mov ecx, O_WRONLY+O_CREAT+O_TRUNC
+ mov edx, 0644o
+ jz @f
+ mov ecx, O_RDONLY
+@@:
+; 3f. Open/create the file, save the handle.
+ push ebx
+ mov ebx, eax
+ kercall __NR_open
+ pop ebx
+ test eax, eax
+ js .fileerr
+ mov [start.in+edi*4], eax
+ inc edi
+.nextarg:
+ dec ebx
+ jnz .parse
+.noargs:
+; 4. End of command line reached. Return.
+ ret
+.fileerr:
+ test edi, edi
+ jz in_openerr
+ jmp out_openerr
+
+; Exit, return code is in al.
+exit:
+ movzx ebx, al
+ push ebx
+ mov eax, [start.buf]
+ test eax, eax
+ jz @f
+ mov ecx, [start.allocated]
+ call free_eax_ecx
+@@:
+ kercall __NR_close, [start.in]
+ kercall __NR_close, [start.out]
+ pop ebx
+ kercall __NR_exit
+
+; Helper procedure for realloc and exit.
+free_eax_ecx:
+ mov ebx, eax
+ kercall __NR_munmap
+ ret
+
+; Output the message given in esi to stderr.
+sayerr:
+ movzx edx, byte [esi-1]
+ kercall __NR_write, 2, esi,
+ ret
+
+; Get environment variable esi (ebx-relative pointer) to the buffer,
+; expanding it if needed.
+get_environment_variable:
+ mov ecx, [start.argc]
+ lea ecx, [start.argv+ecx*4+4]
+.findvar:
+ mov edx, [ecx]
+ add ecx, 4
+ test edx, edx
+ jz .notfound
+ push esi
+ add esi, ebx
+.comparename:
+ lodsb
+ cmp al, [edx]
+ jnz @f
+ inc edx
+ jmp .comparename
+@@:
+ pop esi
+ test al, al
+ jnz .findvar
+ cmp byte [edx], '='
+ jnz .findvar
+ inc edx
+ xor eax, eax
+@@:
+ inc eax
+ cmp byte [edx+eax-1], 0
+ jnz @b
+ stdcall alloc_in_buf, eax
+@@:
+ mov al, [edx]
+ inc edx
+ mov [edi+ebx], al
+ inc edi
+ test al, al
+ jnz @b
+ ret
+.notfound:
+ stdcall alloc_in_buf, 1
+ mov byte [edi+ebx], 0
+ inc edi
+ ret
+
+; Test whether a file with name [.testname] exists.
+; Returns eax<0 if not, nonzero otherwise.
+test_file_exists:
+ push ebx
+ add ebx, [start.testname]
+ sub esp, 800h
+ kercall __NR_stat, , esp
+ add esp, 800h
+ pop ebx
+ ret
diff --git a/data/common/fasmdep/unistd.inc b/data/common/fasmdep/unistd.inc
new file mode 100644
index 0000000000..cefad10833
--- /dev/null
+++ b/data/common/fasmdep/unistd.inc
@@ -0,0 +1,322 @@
+__NR_restart_syscall = 0
+__NR_exit = 1
+__NR_fork = 2
+__NR_read = 3
+__NR_write = 4
+__NR_open = 5
+__NR_close = 6
+__NR_waitpid = 7
+__NR_creat = 8
+__NR_link = 9
+__NR_unlink = 10
+__NR_execve = 11
+__NR_chdir = 12
+__NR_time = 13
+__NR_mknod = 14
+__NR_chmod = 15
+__NR_lchown = 16
+__NR_break = 17
+__NR_oldstat = 18
+__NR_lseek = 19
+__NR_getpid = 20
+__NR_mount = 21
+__NR_umount = 22
+__NR_setuid = 23
+__NR_getuid = 24
+__NR_stime = 25
+__NR_ptrace = 26
+__NR_alarm = 27
+__NR_oldfstat = 28
+__NR_pause = 29
+__NR_utime = 30
+__NR_stty = 31
+__NR_gtty = 32
+__NR_access = 33
+__NR_nice = 34
+__NR_ftime = 35
+__NR_sync = 36
+__NR_kill = 37
+__NR_rename = 38
+__NR_mkdir = 39
+__NR_rmdir = 40
+__NR_dup = 41
+__NR_pipe = 42
+__NR_times = 43
+__NR_prof = 44
+__NR_brk = 45
+__NR_setgid = 46
+__NR_getgid = 47
+__NR_signal = 48
+__NR_geteuid = 49
+__NR_getegid = 50
+__NR_acct = 51
+__NR_umount2 = 52
+__NR_lock = 53
+__NR_ioctl = 54
+__NR_fcntl = 55
+__NR_mpx = 56
+__NR_setpgid = 57
+__NR_ulimit = 58
+__NR_oldolduname = 59
+__NR_umask = 60
+__NR_chroot = 61
+__NR_ustat = 62
+__NR_dup2 = 63
+__NR_getppid = 64
+__NR_getpgrp = 65
+__NR_setsid = 66
+__NR_sigaction = 67
+__NR_sgetmask = 68
+__NR_ssetmask = 69
+__NR_setreuid = 70
+__NR_setregid = 71
+__NR_sigsuspend = 72
+__NR_sigpending = 73
+__NR_sethostname = 74
+__NR_setrlimit = 75
+__NR_getrlimit = 76
+__NR_getrusage = 77
+__NR_gettimeofday = 78
+__NR_settimeofday = 79
+__NR_getgroups = 80
+__NR_setgroups = 81
+__NR_select = 82
+__NR_symlink = 83
+__NR_oldlstat = 84
+__NR_readlink = 85
+__NR_uselib = 86
+__NR_swapon = 87
+__NR_reboot = 88
+__NR_readdir = 89
+__NR_mmap = 90
+__NR_munmap = 91
+__NR_truncate = 92
+__NR_ftruncate = 93
+__NR_fchmod = 94
+__NR_fchown = 95
+__NR_getpriority = 96
+__NR_setpriority = 97
+__NR_profil = 98
+__NR_statfs = 99
+__NR_fstatfs = 100
+__NR_ioperm = 101
+__NR_socketcall = 102
+__NR_syslog = 103
+__NR_setitimer = 104
+__NR_getitimer = 105
+__NR_stat = 106
+__NR_lstat = 107
+__NR_fstat = 108
+__NR_olduname = 109
+__NR_iopl = 110
+__NR_vhangup = 111
+__NR_idle = 112
+__NR_vm86old = 113
+__NR_wait4 = 114
+__NR_swapoff = 115
+__NR_sysinfo = 116
+__NR_ipc = 117
+__NR_fsync = 118
+__NR_sigreturn = 119
+__NR_clone = 120
+__NR_setdomainname = 121
+__NR_uname = 122
+__NR_modify_ldt = 123
+__NR_adjtimex = 124
+__NR_mprotect = 125
+__NR_sigprocmask = 126
+__NR_create_module = 127
+__NR_init_module = 128
+__NR_delete_module = 129
+__NR_get_kernel_syms = 130
+__NR_quotactl = 131
+__NR_getpgid = 132
+__NR_fchdir = 133
+__NR_bdflush = 134
+__NR_sysfs = 135
+__NR_personality = 136
+__NR_afs_syscall = 137
+__NR_setfsuid = 138
+__NR_setfsgid = 139
+__NR__llseek = 140
+__NR_getdents = 141
+__NR__newselect = 142
+__NR_flock = 143
+__NR_msync = 144
+__NR_readv = 145
+__NR_writev = 146
+__NR_getsid = 147
+__NR_fdatasync = 148
+__NR__sysctl = 149
+__NR_mlock = 150
+__NR_munlock = 151
+__NR_mlockall = 152
+__NR_munlockall = 153
+__NR_sched_setparam = 154
+__NR_sched_getparam = 155
+__NR_sched_setscheduler = 156
+__NR_sched_getscheduler = 157
+__NR_sched_yield = 158
+__NR_sched_get_priority_max = 159
+__NR_sched_get_priority_min = 160
+__NR_sched_rr_get_interval = 161
+__NR_nanosleep = 162
+__NR_mremap = 163
+__NR_setresuid = 164
+__NR_getresuid = 165
+__NR_vm86 = 166
+__NR_query_module = 167
+__NR_poll = 168
+__NR_nfsservctl = 169
+__NR_setresgid = 170
+__NR_getresgid = 171
+__NR_prctl = 172
+__NR_rt_sigreturn = 173
+__NR_rt_sigaction = 174
+__NR_rt_sigprocmask = 175
+__NR_rt_sigpending = 176
+__NR_rt_sigtimedwait = 177
+__NR_rt_sigqueueinfo = 178
+__NR_rt_sigsuspend = 179
+__NR_pread64 = 180
+__NR_pwrite64 = 181
+__NR_chown = 182
+__NR_getcwd = 183
+__NR_capget = 184
+__NR_capset = 185
+__NR_sigaltstack = 186
+__NR_sendfile = 187
+__NR_getpmsg = 188
+__NR_putpmsg = 189
+__NR_vfork = 190
+__NR_ugetrlimit = 191
+__NR_mmap2 = 192
+__NR_truncate64 = 193
+__NR_ftruncate64 = 194
+__NR_stat64 = 195
+__NR_lstat64 = 196
+__NR_fstat64 = 197
+__NR_lchown32 = 198
+__NR_getuid32 = 199
+__NR_getgid32 = 200
+__NR_geteuid32 = 201
+__NR_getegid32 = 202
+__NR_setreuid32 = 203
+__NR_setregid32 = 204
+__NR_getgroups32 = 205
+__NR_setgroups32 = 206
+__NR_fchown32 = 207
+__NR_setresuid32 = 208
+__NR_getresuid32 = 209
+__NR_setresgid32 = 210
+__NR_getresgid32 = 211
+__NR_chown32 = 212
+__NR_setuid32 = 213
+__NR_setgid32 = 214
+__NR_setfsuid32 = 215
+__NR_setfsgid32 = 216
+__NR_pivot_root = 217
+__NR_mincore = 218
+__NR_madvise = 219
+__NR_madvise1 = 219
+__NR_getdents64 = 220
+__NR_fcntl64 = 221
+__NR_gettid = 224
+__NR_readahead = 225
+__NR_setxattr = 226
+__NR_lsetxattr = 227
+__NR_fsetxattr = 228
+__NR_getxattr = 229
+__NR_lgetxattr = 230
+__NR_fgetxattr = 231
+__NR_listxattr = 232
+__NR_llistxattr = 233
+__NR_flistxattr = 234
+__NR_removexattr = 235
+__NR_lremovexattr = 236
+__NR_fremovexattr = 237
+__NR_tkill = 238
+__NR_sendfile64 = 239
+__NR_futex = 240
+__NR_sched_setaffinity = 241
+__NR_sched_getaffinity = 242
+__NR_set_thread_area = 243
+__NR_get_thread_area = 244
+__NR_io_setup = 245
+__NR_io_destroy = 246
+__NR_io_getevents = 247
+__NR_io_submit = 248
+__NR_io_cancel = 249
+__NR_fadvise64 = 250
+__NR_exit_group = 252
+__NR_lookup_dcookie = 253
+__NR_epoll_create = 254
+__NR_epoll_ctl = 255
+__NR_epoll_wait = 256
+__NR_remap_file_pages = 257
+__NR_set_tid_address = 258
+__NR_timer_create = 259
+__NR_statfs64 = 268
+__NR_fstatfs64 = 269
+__NR_tgkill = 270
+__NR_utimes = 271
+__NR_fadvise64_64 = 272
+__NR_vserver = 273
+__NR_mbind = 274
+__NR_get_mempolicy = 275
+__NR_set_mempolicy = 276
+__NR_mq_open = 277
+__NR_kexec_load = 283
+__NR_waitid = 284
+__NR_sys_setaltroot = 285
+__NR_add_key = 286
+__NR_request_key = 287
+__NR_keyctl = 288
+__NR_ioprio_set = 289
+__NR_ioprio_get = 290
+__NR_inotify_init = 291
+__NR_inotify_add_watch = 292
+__NR_inotify_rm_watch = 293
+__NR_migrate_pages = 294
+__NR_openat = 295
+__NR_mkdirat = 296
+__NR_mknodat = 297
+__NR_fchownat = 298
+__NR_futimesat = 299
+__NR_fstatat64 = 300
+__NR_unlinkat = 301
+__NR_renameat = 302
+__NR_linkat = 303
+__NR_symlinkat = 304
+__NR_readlinkat = 305
+__NR_fchmodat = 306
+__NR_faccessat = 307
+__NR_pselect6 = 308
+__NR_ppoll = 309
+__NR_unshare = 310
+__NR_set_robust_list = 311
+__NR_get_robust_list = 312
+__NR_splice = 313
+__NR_sync_file_range = 314
+__NR_tee = 315
+__NR_vmsplice = 316
+__NR_move_pages = 317
+__NR_getcpu = 318
+__NR_epoll_pwait = 319
+__NR_utimensat = 320
+__NR_signalfd = 321
+__NR_timerfd_create = 322
+__NR_eventfd = 323
+__NR_fallocate = 324
+__NR_timerfd_settime = 325
+__NR_timerfd_gettime = 326
+__NR_signalfd4 = 327
+__NR_eventfd2 = 328
+__NR_epoll_create1 = 329
+__NR_dup3 = 330
+__NR_pipe2 = 331
+__NR_inotify_init1 = 332
+__NR_preadv = 333
+__NR_pwritev = 334
+__NR_rt_tgsigqueueinfo = 335
+__NR_perf_event_open = 336
diff --git a/data/common/fasmdep/windows_header.inc b/data/common/fasmdep/windows_header.inc
new file mode 100644
index 0000000000..41493b6a09
--- /dev/null
+++ b/data/common/fasmdep/windows_header.inc
@@ -0,0 +1,6 @@
+; Header for Windows program
+ format PE console
+include 'win32w.inc'
+ERROR_BROKEN_PIPE = 109
+ERROR_MORE_DATA = 234
+section '.text' code readable executable
diff --git a/data/common/fasmdep/windows_sys.inc b/data/common/fasmdep/windows_sys.inc
new file mode 100644
index 0000000000..444259a929
--- /dev/null
+++ b/data/common/fasmdep/windows_sys.inc
@@ -0,0 +1,225 @@
+; Platform-specific procedures for Windows.
+
+; Reallocate memory at pointer [start.buf] and size [start.allocated],
+; new size is in eax.
+realloc:
+ push eax
+ stdcall [VirtualAlloc], 0, eax, MEM_COMMIT + MEM_RESERVE, PAGE_READWRITE
+ pop ecx
+ test eax, eax
+ jz nomemory
+ add [start.free], ecx
+ xchg ecx, [start.allocated]
+ sub [start.free], ecx
+ push esi edi
+ mov edi, eax
+ xchg eax, [start.buf]
+ shr ecx, 2
+ jz .nothing
+ mov esi, eax
+ rep movsd
+ call free_eax
+.nothing:
+ pop edi esi
+ ret
+
+; Read the next portion of input data to [start.buf].
+read:
+ push eax ; reserve space for *lpNumberOfBytesRead
+ mov ecx, esp
+ mov eax, [start.buf]
+ add eax, [start.allocated]
+ sub eax, [start.free]
+ stdcall [ReadFile], [start.in], eax, [start.free], ecx, 0
+ test eax, eax
+ jz @f
+.nothing:
+ pop eax
+ ret
+@@:
+; ERROR_BROKEN_PIPE and ERROR_MORE_DATA are normal codes when dealing with pipes
+ call [GetLastError]
+ cmp eax, ERROR_BROKEN_PIPE
+ jz .nothing
+ cmp eax, ERROR_MORE_DATA
+ jz .nothing
+ pop eax
+ jmp readerr
+
+; Write output data: eax=pointer, edi=size.
+write:
+ push eax
+ mov ecx, esp
+ stdcall [WriteFile], [start.out], eax, edi, ecx, 0
+ test eax, eax
+ pop eax
+ jz writeerr
+ cmp eax, edi
+ jnz writeerr
+ ret
+
+; Parse command line, open input and output files.
+get_params:
+; 1. Get the command line split to argv[] array.
+ call [GetCommandLineW]
+ push eax ; reserve space for *pNumArgs
+ stdcall [CommandLineToArgvW], eax, esp
+ pop ebx ; ebx = argc, eax = argv
+ push eax ; save argument for [LocalFree]
+; 2. Prepare for scanning, skipping argv[0].
+ cmp ebx, 1
+ jbe .noargs
+ dec ebx
+ lea esi, [eax+4] ; skip argv[0]
+ xor edi, edi ; no args parsed yet
+; 3. Parse loop.
+.parse:
+; 3a. Get the next argument.
+ lodsd
+; 3b. Check whether it is a known option.
+ cmp dword [eax], '-' + 'e' * 65536
+ jnz @f
+ cmp word [eax+4], 0
+ jnz @f
+; 3c. If it is, modify flags and continue the loop.
+ mov [start.flags], 1 ; '-e' is given
+ jmp .nextarg
+@@:
+; 3d. Otherwise, it is a name of input or output file.
+; edi keeps the count of names encountered before;
+; edi = 0 means this is input file, edi = 1 - output file,
+; otherwise this is third arg, which is an error
+ cmp edi, 1
+ ja .toomanyargs
+; 3e. Some parameters of CreateFileW differ for input and output. Setup them.
+ mov ecx, GENERIC_WRITE
+ mov edx, CREATE_ALWAYS
+ jz @f
+ add ecx, ecx ; GENERIC_READ
+ inc edx ; OPEN_EXISTING
+@@:
+; 3f. Open/create the file, save the handle.
+ stdcall [CreateFileW], eax, ecx, FILE_SHARE_READ+FILE_SHARE_DELETE, 0, edx, FILE_ATTRIBUTE_NORMAL, 0
+ cmp eax, INVALID_HANDLE_VALUE
+ jz .fileerr
+ mov [start.in+edi*4], eax
+ inc edi
+.nextarg:
+ dec ebx
+ jnz .parse
+.noargs:
+; 4. End of command line reached. If input and/or output was not given, use defaults.
+ test edi, edi
+ jnz .hasinput
+ stdcall [GetStdHandle], STD_INPUT_HANDLE
+ mov [start.in], eax
+.hasinput:
+ cmp edi, 1
+ ja .hasoutput
+ stdcall [GetStdHandle], STD_OUTPUT_HANDLE
+ mov [start.out], eax
+.hasoutput:
+; 5. Free memory allocated in step 1 and return.
+ call [LocalFree]
+ ret
+.toomanyargs:
+ call [LocalFree]
+ jmp information
+.fileerr:
+ call [LocalFree]
+ test edi, edi
+ jz in_openerr
+ jmp out_openerr
+
+; Exit, return code is in al.
+exit:
+ movzx eax, al
+ push eax ; save return code for [ExitProcess]
+ mov eax, [start.buf]
+ test eax, eax
+ jz @f
+ call free_eax
+@@:
+ stdcall [CloseHandle], [start.in]
+ stdcall [CloseHandle], [start.out]
+ call [ExitProcess]
+
+; Output the message given in esi to stderr.
+sayerr:
+ stdcall [GetStdHandle], STD_ERROR_HANDLE
+ push eax
+ mov ecx, esp
+ movzx edx, byte [esi-1]
+ stdcall [WriteFile], eax, esi, edx, ecx, 0
+ pop ecx
+ ret
+
+; Helper procedure for realloc and exit.
+free_eax:
+ stdcall [VirtualFree], eax, 0, MEM_RELEASE
+ ret
+
+; Get environment variable esi (ebx-relative pointer) to the buffer,
+; expanding it if needed.
+get_environment_variable:
+ lea eax, [edi+ebx]
+ lea ecx, [esi+ebx]
+ stdcall [GetEnvironmentVariableA], ecx, eax, [start.free]
+; GetEnvironmentVariable returns one of following values:
+; * if all is ok: number of characters copied to the buffer,
+; not including terminating zero
+; * if buffer size is too small: number of characters required in the buffer,
+; including terminating zero
+; * if environment variable not found: zero
+; We treat the last case the same as first one.
+ cmp eax, [start.free]
+ jae .resize
+ inc eax ; include terminating zero
+ add edi, eax
+ sub [start.free], eax
+ mov byte [edi+ebx-1], 0 ; force zero-terminated or empty string
+ ret
+.resize:
+; esi can be inside the buffer or outside the buffer;
+; we must not correct it in the first case,
+; but advance relative to new value of ebx in the second one.
+ mov ecx, esi
+ cmp esi, [start.allocated]
+ jb @f
+ add esi, ebx
+@@:
+ stdcall alloc_in_buf, eax
+ cmp esi, ecx
+ jz get_environment_variable
+ sub esi, ebx
+ jmp get_environment_variable
+
+; Test whether a file with name [.testname] exists.
+; Returns eax<0 if not, nonzero otherwise.
+test_file_exists:
+ mov eax, [start.testname]
+ add eax, ebx
+ stdcall [GetFileAttributesA], eax
+ inc eax
+ ret
+
+; Imports
+align 4
+data import
+library kernel32,'kernel32.dll',shell32,'shell32.dll'
+import kernel32, \
+ GetLastError, 'GetLastError', \
+ ExitProcess, 'ExitProcess', \
+ VirtualAlloc, 'VirtualAlloc', \
+ VirtualFree, 'VirtualFree', \
+ LocalFree, 'LocalFree', \
+ GetStdHandle, 'GetStdHandle', \
+ CreateFileW, 'CreateFileW', \
+ GetFileAttributesA, 'GetFileAttributesA', \
+ ReadFile, 'ReadFile', \
+ WriteFile, 'WriteFile', \
+ CloseHandle, 'CloseHandle', \
+ GetCommandLineW, 'GetCommandLineW', \
+ GetEnvironmentVariableA, 'GetEnvironmentVariableA'
+import shell32, CommandLineToArgvW, 'CommandLineToArgvW'
+end data
diff --git a/data/eng/Makefile b/data/eng/Makefile
index a061377b38..772df2e9df 100644
--- a/data/eng/Makefile
+++ b/data/eng/Makefile
@@ -455,7 +455,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
-docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
+docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(KERNEL)/docs/sysfuncs.txt
cp $(KERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT
diff --git a/data/eng/Makefile.fasm b/data/eng/Makefile.fasm
index 7e81ac5935..67f4b4debe 100644
--- a/data/eng/Makefile.fasm
+++ b/data/eng/Makefile.fasm
@@ -28,22 +28,20 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
@@ -54,10 +52,8 @@ $(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbin
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
- fasm -m 65536 "$<" "$@" -s .deps/kernel.fas
- prepsrc .deps/kernel.fas /dev/null
- prepsrc .deps/kernel.fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
- kerpack $@
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$<" "$@" -s $$tmpfile && \
+ fasmdep -e $$tmpfile > .deps/kernel.Po && \
+ rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po
diff --git a/data/new-stack/Makefile b/data/new-stack/Makefile
index 1ae9f71b30..d06ba98b14 100644
--- a/data/new-stack/Makefile
+++ b/data/new-stack/Makefile
@@ -299,7 +299,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
-docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
+docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(TRUNKKERNEL)/docs/sysfuncs.txt
cp $(TRUNKKERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT
diff --git a/data/new-stack/Makefile.fasm b/data/new-stack/Makefile.fasm
index 26e456d6e3..67f4b4debe 100644
--- a/data/new-stack/Makefile.fasm
+++ b/data/new-stack/Makefile.fasm
@@ -28,25 +28,32 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 $$< "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
+define fasm_nokpack_meta_rule
+$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
+-include .deps/$(4).Po
+endef
+
progname=$(call respace,$(basename $(notdir $(call binarypart,$(f)))))
binarydir=$(subst ./,,$(dir $(call binarypart,$(f))))
-$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
+$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD) $(SKIN_SOURCES),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
+$(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
- fasm -m 65536 $< "$@" -s .deps/kernel.fas
- prepsrc .deps/kernel.fas /dev/null
- prepsrc .deps/kernel.fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
- kerpack $@
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$<" "$@" -s $$tmpfile && \
+ fasmdep -e $$tmpfile > .deps/kernel.Po && \
+ rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po
diff --git a/data/rus/Makefile b/data/rus/Makefile
index d5e69e4838..fd997947ed 100644
--- a/data/rus/Makefile
+++ b/data/rus/Makefile
@@ -474,7 +474,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncr.txt - it isn't directly included in the image.
-docpack: $(DOCDIR)SYSFUNCR.TXT $(wildcard $(DOCDIR)*)
+docpack: $(DOCDIR)SYSFUNCR.TXT
$(DOCDIR)SYSFUNCR.TXT: $(KERNEL)/docs/sysfuncr.txt
cp $(KERNEL)/docs/sysfuncr.txt $(DOCDIR)SYSFUNCR.TXT
diff --git a/data/rus/Makefile.fasm b/data/rus/Makefile.fasm
index 7e81ac5935..67f4b4debe 100644
--- a/data/rus/Makefile.fasm
+++ b/data/rus/Makefile.fasm
@@ -28,22 +28,20 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
- fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
- prepsrc .deps/$(4).fas /dev/null
- prepsrc .deps/$(4).fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
+ fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
+ rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
@@ -54,10 +52,8 @@ $(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbin
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
- fasm -m 65536 "$<" "$@" -s .deps/kernel.fas
- prepsrc .deps/kernel.fas /dev/null
- prepsrc .deps/kernel.fas /dev/stdout | \
- perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
- -e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
- kerpack $@
+ tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
+ (fasm -m 65536 "$<" "$@" -s $$tmpfile && \
+ fasmdep -e $$tmpfile > .deps/kernel.Po && \
+ rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po