MS Windows x64 Application Binary Interface macros.
This library contains macros for 64bit MS Windows ABI invokations, retrieving command-line arguments, standard I/O and program termination.
Macro names in this 64bit library.winabi.htmare identical with macros from 32bit library winapi.htm . I don't expect to include both 32bit and 64bit version in the same source.
winabi HEAD INCLUDE1 winansi.htm ; Make sure that %WinANSI is assigned before WinAPI invokation.
%Param5 %SET RBP+48 %Param4 %SET RBP+40 %Param3 %SET RBP+32 %Param2 %SET RBP+24 %Param1 %SET RBP+16
This macroinstruction invokes Function exported by [WindowsAPI].
The macro is similar to FastCall's Invoke with two minor differences:
Lib=
.64bit WinAPI functions do not keep the original contents of flags, RAX, RCX, RDX, R8..R11. They require
Direction Flag be zero on input.
Epilogue code forces this macro WinAPI to return DF=CF=ZF=0.
ABI specification requires that stack is OWORD-aligned at the moment
when CALL instruction is just about to begin. This means that when the CALL
has just been executed and RIP points to the first instruction of called function,
RSP is OWORD-misaligned by QWORD, in other words its least significant bits are 1000b.
Before and after invokation of macro WinAPI the stack pointer must the same, of course.
This is achieved by pushing one or two copies of original RSP contents.
The number of pushed copies depends on RSP alignment at run-time.
Although Windows ABI specifies fastcall convention with %1..%4 delivered in RCX,RDX,R8,R9,
arguments of this macro may be supplied in those registers too, e.g.
WinAPI MessageBox,RDX,R8,R8,MB_OK
in arbitrary order,
because arguments are pushed on stack by this macro
before being loaded to input registers RCX,RDX,R8,R9.
kernel32.dll, or
IMPORT Function, LIB=user32.dll
, orWinAPI %MACRO Function, Argument1, Argument2,,, Lib= %IF %# = 1 ; If no arguments. %ArgList %SET RCX,RDX,R8,R9 %ENDIF %IF %# = 2 ; If one argument. %ArgList %SET %2,RDX,R8,R9 %ENDIF %IF %# = 3 ; If two arguments. %ArgList %SET %2,%3,R8,R9 %ENDIF %IF %# = 4 ; If three arguments. %ArgList %SET %2,%3,%4,R9 %ENDIF %IF %# >= 5 ; If four or more arguments. %ArgList %SET %*{2..} %ENDIF %ArgNr %SETL %ArgList ; Number of arguments, at least 4. PUSH RSP ; Save original stack pointer value. TEST SPL,1000b ; Test stack OWORD alignment at run-time. %IF %ArgNr & 1b ; When the number of arguments is odd, JNZ .WinAPI%.: ; skip padding if RSP is unaligned. %ELSE ; When the number of arguments is even, JZ .WinAPI%.: ; skip padding if RSP is OWORD aligned. %ENDIF PUSH RSP ; Save and update copy of original stack pointer value. ADDQ [RSP],8 .WinAPI%.: %WHILE %ArgNr > 0 PUSHQ %ArgList{%ArgNr} ; Push all arguments on stack, begin with the last one. %ArgNr %SETA %ArgNr-1 %ENDWHILE %IF '%ArgList{1}' !== 'RCX' MOV RCX,[RSP+0] ; Load first four arguments to registers due to FastCall convention. %ENDIF %IF '%ArgList{2}' !== 'RDX' MOV RDX,[RSP+8] %ENDIF %IF '%ArgList{3}' !== 'R8' MOV R8,[RSP+16] %ENDIF %IF '%ArgList{4}' !== 'R9' MOV R9,[RSP+24] %ENDIF reg %IF TYPE# %Function = 'R'; Test if the Function is specified in register. %IF '%Function'=='RCX'||"%Function"=="RDX"||"%Function"=="R8"||"%Function"=="R9" %ERROR ID=5956,'WinAPI function cannot be supplied in scratch register %Function.' %EXITMACRO WinAPI %ENDIF CALL %Function %ELSE reg ; The Function is specified by its name. %suffix %SET ; First assume there is no A|W suffix. fn %FOR %WinANSI ; Examine if Function is on the list %WinANSI. %IF '%fn' === '%Function' %suffix %SETC ('W' & (%^UNICODE)) + ('A' & ~(%^UNICODE)) %EXITFOR fn ; No need for further investigation of the list. %ENDIF %ENDFOR fn ; %suffix is now A or W or empty. IMPORT %Function%suffix, Lib=%Lib CALL %Function%suffix %ENDIF reg %ArgNr %SETL %ArgList ; Reload the number of arguments. LEA RSP,[RSP+8*%ArgNr] ; Let RSP skip all pushed arguments POP RSP ; and restore its original value. %ENDMACRO WinAPI
GetArgCount %MACRO CALL GetArgCount@RT GetArgCount@RT PROC1 PUSH RCX,RDX,R8,R9,R10,R11 MOV RBP,RSP %GetArgWithin %SETA 1 ; Boolean parsing flags will be kept in DL. %GetArgInQuotes %SETA 2 %GetArgCommaTerm %SETA 4 WinAPI GetCommandLineA MOV RSI,RAX ; RSI now points to the entire command-line ANSI zero-terminated string. SUB RCX,RCX ; RCX will keep the ordinal number of parsed cmdline argument. SUB DL,DL ; Flags in DL will be used as a parsing status register. TEST ESI,ESI JZ .80: ; If API returned FALSE. DEC RCX .10: LODSB CMP AL,' ' JBE .30: CMP AL,',' JE .30: AND DL,~%GetArgCommaTerm CMP AL,'"' JNE .20: XOR DL,%GetArgInQuotes .20: TEST DL,%GetArgWithin JNZ .10: OR DL,%GetArgWithin INC RCX JMP .10: .30: TEST DL,%GetArgInQuotes JZ .40: CMP AL,0 JNE .10: JMP .80: .40: TEST DL,%GetArgWithin JZ .50: AND DL,~(%GetArgWithin|%GetArgCommaTerm) CMP AL,',' JNE .70: OR DL,%GetArgCommaTerm JMP .70: .50: CMP AL,0 JE .80: CMP AL,',' JNE .10: TEST DL,%GetArgCommaTerm JNZ .60: OR DL,%GetArgCommaTerm JMP .10: .60: INC RCX .70: CMP AL,0 JNE .10: .80: TEST DL,%GetArgInQuotes JZ .90: STC ; If premature end of command line. .90: MOV RAX,RCX POP R11,R10,R9,R8,RDX,RCX RET ENDPROC1 GetArgCount@RT %ENDMACRO GetArgCount
Macro GetArg retrieves ArgNumber-th parameter provided on command line.
Parameters on the command line may be separated with unquoted
white spaces or commas. Single aphostrophe cannot be used as quote.
Macro returnes the executable name itself when ArgNumber is 0.
It is taken verbatim from the console window or, if launched
from Explorer, it may be expanded to a full pathname.
The returned argument is not zero terminated and it is not writable. Make a copy in local memory if you need to modify it. Quotes surrounding the argument are returned, too.
Value of EUROASM UNICODE=
option specifies whether the returned string will be ANSI or WIDE.
GetArg %MACRO ArgNumber PUSHQ %ArgNumber %IF %^UNICODE CALL GetArgW@RT GetArgW@RT PROC1 PUSHQ RAX,RDX,R8,R9,R10,R11 %GetArgNumber %SET RSP+56 %GetArgWithin %SETA 1 ; Parsing within argument string. Boolean flags will be kept in DL. %GetArgInQuotes %SETA 2 ; Parsing within quoted argument string. %GetArgCommaTerm %SETA 4 ; Previous argument was comma-terminated. WinAPI GetCommandLineW MOV RSI,RAX ; RSI now points to the entire command-line WIDE zero-terminated string. SUB RCX,RCX ; RCX will keep the ordinal number of parsed cmdline argument. SUB R8,R8 ; R8 will be pointer to argument. SUB R9,R9 ; R9 will be pointer to the end of argument. SUB DL,DL ; DL will be used as a parsing flag register. CMP RSI,R8 ; Is the pointer valid? JZ .80 ; If API returned FALSE, abort with CF. DEC RCX ; This first argument has ordinal 0 (executable name). MOV R8,RSI .10: LODSW ; Parse the next character. CMP AX,' ' JBE .30 CMP AX,',' JE .30: AND DL,~%GetArgCommaTerm CMP AX,'"' JNE .20: XOR DL,%GetArgInQuotes .20: TEST DL,%GetArgWithin JNZ .10: OR DL,%GetArgWithin INC RCX ; Started to parse RCX-th argument now. LEA R8,[RSI-2] ; Remember in R8 where this argument begins. JMP .10: .30: TEST DL,%GetArgInQuotes JZ .40: CMP AX,0 JNE .10: JMP .80: .40: TEST DL,%GetArgWithin JZ .50: AND DL,~(%GetArgWithin|%GetArgCommaTerm) ; Character in AX (comma or space or 0) terminates the argument. LEA R9,[RSI-2] ; End of argument. CMP AX,',' JNE .70: OR DL,%GetArgCommaTerm JMP .70: .50: CMP AX,0 JE .80: CMP AX,',' JNE .10: TEST DL,%GetArgCommaTerm JNZ .60: OR DL,%GetArgCommaTerm JMP .10: .60: LEA R9,[RSI-2] ; Pointer to the end of argument. INC RCX .70: CMP RCX,[%GetArgNumber] ; Have we reached the desired argument? JE .90: CMP AX,0 JNE .10: .80: STC ; If premature end of command line. .90: PUSHF ; Temporarily save CF. MOV RCX,R9 ; Return string R8..R9 as RSI,RCX. MOV RSI,R8 JNC .95: MOV R8,RCX ; Force RCX=0 on error. .95: SUB RCX,R8 POPF ; Restore CF. POP R11,R10,R9,R8,RDX,RAX RET 8 ENDPROC1 GetArgW@RT %ELSE ; It not %^UNICODE. CALL GetArgA@RT GetArgA@RT PROC1 PUSH RAX,RDX,R8,R9,R10,R11 %GetArgNumber %SET RSP+56 %GetArgWithin %SETA 1 ; Parsing within argument string. Boolean flags will be kept in DL. %GetArgInQuotes %SETA 2 ; Parsing within quoted argument string. %GetArgCommaTerm %SETA 4 ; Previous argument was comma-terminated. WinAPI GetCommandLineA MOV RSI,RAX ; RSI now points to the entire command-line ANSI zero-terminated string. SUB RCX,RCX ; RCX will keep the ordinal number of parsed cmdline argument. SUB R8,R8 ; R8 will be pointer to argument. SUB R9,R9 ; R9 will be pointer to the end of argument. SUB DL,DL ; DL will be used as a parsing flag register. CMP RSI,R8 ; Is the pointer valid? JZ .80 ; If API returned FALSE, abort with CF. DEC RCX ; This first argument has ordinal 0 (executable name). .10: LODSB ; Parse the next character. CMP AL,' ' JBE .30: CMP AL,',' JE .30: AND DL,~%GetArgCommaTerm CMP AL,'"' JNE .20: XOR DL,%GetArgInQuotes .20: TEST DL,%GetArgWithin JNZ .10: OR DL,%GetArgWithin INC RCX ; Started to parse RCX-th argument now. LEA R8,[RSI-1] ; Remember in R8 where this argument begins. JMP .10: .30: TEST DL,%GetArgInQuotes JZ .40: CMP AL,0 JNE .10: JMP .80: ; Abort if the closing quote is missing. .40: TEST DL,%GetArgWithin JZ .50: AND DL,~(%GetArgWithin|%GetArgCommaTerm) ; Character in AL (comma or space or 0) terminates the argument. LEA R9,[RSI-1] ; End of argument. CMP AL,',' JNE .70: OR DL,%GetArgCommaTerm JMP .70: .50: CMP AL,0 JE .80: CMP AL,',' JNE .10: TEST DL,%GetArgCommaTerm JNZ .60: OR DL,%GetArgCommaTerm JMP .10: .60: LEA R9,[RSI-1] ; Pointer to the end of argument. INC RCX .70: CMP RCX,[%GetArgNumber] ; Have we reached the desired argument? JE .90: CMP AL,0 JNE .10: .80: STC ; If premature end of command line. .90: PUSHF ; Temporarily save CF. MOV RCX,R9 ; Return string R8..R9 as RSI,RCX. MOV RSI,R8 JNC .95: MOV R8,RCX ; Force RCX=0 on error. .95: SUB RCX,R8 POPF ; Restore CF. POP R11,R10,R9,R8,RDX,RAX RET 8 ENDPROC1 GetArgA@RT %ENDIF %ENDMACRO GetArg
Macro StdOutput writes one or more concatenated strings to the standard output or to other equipment specified with the Handle identifier.
Strings are either zero-terminated, or the keyword Size= must specify its size in bytes. The terminating NUL character is never written.
If keyword Eol=Yes, macro writes CR+LF after all strings.
One of four possible runtime subprocesures is selected to emit, depending on the chosen ANSI/WIDE and File/Console options.
Output of WriteFile (default) is redirectable, but it writes WIDE string as is; in OEM console are the UTF-16 encoded characters displayed as interlaced.Unicode= %^UNICODE is boolean specification whether the Strings are in WIDE (UTF-16) encoding.
Output produced by WriteConsole (when Console=Yes) cannot be redirected by command-line operator > but it accepts WIDE Unicode strings and displays the text in TrueType console properly, including non-English characters.
StdOutput %MACRO String1,String2,,,Size=-1, Handle=-11, Eol=No, Unicode=%^UNICODE, Console=No %IF %Unicode ; WIDE variant. %StdOutputRT %SET StdOutputW %ELSE ; ANSI variant. %StdOutputRT %SET StdOutputA %ENDIF %IF "%Console[1]" == "N" ; WriteFile variant. %StdOutputRT %SET %StdOutputRT[]F@RT %ELSE ; WriteConsole variant. %StdOutputRT %SET %StdOutputRT[]C@RT %ENDIF ; %StdOutputRT is now the name of runtime PROC1. OpNr %FOR 1..%#, STEP=1 PUSHQ %Handle, %Size, %1 CALL %StdOutputRT %SHIFT 1 ; The next string to output. %ENDFOR OpNr %IF "%Eol[1]"!=="N" %IF %Unicode ; Write EOL in WIDE variant. PUSHQ %Handle, 4, =D(0x000A000D) CALL %StdOutputRT %ELSE ; Write EOL in ANSI variant. PUSHQ %Handle, 2, =W(0x0A0D) CALL %StdOutputRT %ENDIF %ENDIF WC %IF "%StdOutputRT" === "StdOutputWC@RT" StdOutputWC@RT:: PROC1 ; WIDE Console variant. PUSHQ RAX,RCX,RDX,R8,R9,R10,R11 %StdOutputHandle %SET RSP+80 %StdOutputSize %SET RSP+72 %StdOutputString %SET RSP+64 MOV RAX,[%StdOutputHandle] WinAPI GetStdHandle,RAX ; Output handle identifier. MOV R10,RAX ; Save the (redirected) handle to R10. INC RAX ; Test on INVALID_HANDLE_VALUE (-1). STC JZ .90: ; Abort with CF when INVALID_HANDLE_VALUE. MOV RDI,[%StdOutputString] ; String pointer. MOV RCX,[%StdOutputSize] ; String maximal size. SUB EAX,EAX SAR RCX,1 ; Convert size in bytes to characters. MOV R8,RDI ; Remember start of string in R8. REPNE SCASW ; Find the end of string. JNE .10: SUB RDI,2 ; Skip the NUL character. .10: SUB RDI,R8 ; RDI is now string size in bytes. PUSH RDX ; Make room for the written-size DWORD. MOV RDX,RSP SHR RDI,1 ; RDI is now string size in characters. WinAPI WriteConsoleW,R10,R8,RDI,RDX,0 POP RDX CMP RDX,RDI ; Set CF if not all characters were written. .90: POP R11,R10,R9,R8,RDX,RCX,RAX RET 3*8 ENDP1 StdOutputWC@RT:: %ENDIF WC WF %IF "%StdOutputRT" === "StdOutputWF@RT" StdOutputWF@RT:: PROC1 ; WIDE File variant. PUSHQ RAX,RCX,RDX,R8,R9,R10,R11 %StdOutputHandle %SET RSP+80 %StdOutputSize %SET RSP+72 %StdOutputString %SET RSP+64 MOV RAX,[%StdOutputHandle] WinAPI GetStdHandle,RAX ; Output handle identifier. MOV R10,RAX ; Save the (redirected) handle to R10. INC RAX ; Test on INVALID_HANDLE_VALUE (-1). STC JZ .90: ; Abort with CF when INVALID_HANDLE_VALUE. MOV RDI,[%StdOutputString] ; String pointer. MOV RCX,[%StdOutputSize] ; String maximal size. SUB EAX,EAX SAR RCX,1 ; Convert size in bytes to characters. MOV R8,RDI ; Remember start of string in R8. REPNE SCASW ; Find the end of string. JNE .10: SUB RDI,2 ; Skip the NUL character. .10: SUB RDI,R8 ; RDI is now string size in bytes. PUSH RDX ; Make room for the written-size DWORD. MOV RDX,RSP WinAPI WriteFile,R10,R8,RDI,RDX,0 POP RDX CMP RDX,RDI ; Set CF if not all characters were written. .90: POP R11,R10,R9,R8,RDX,RCX,RAX RET 3*8 ENDP1 StdOutputWF@RT:: %ENDIF WF AC %IF "%StdOutputRT" === "StdOutputAC@RT" StdOutputAC@RT:: PROC1 ; ANSI Console variant. PUSHQ RAX,RCX,RDX,R8,R9,R10,R11 %StdOutputHandle %SET RSP+80 %StdOutputSize %SET RSP+72 %StdOutputString %SET RSP+64 MOV RAX,[%StdOutputHandle] WinAPI GetStdHandle,RAX ; Output handle identifier. MOV R10,RAX ; Save the (redirected) handle to R10. INC RAX ; Test on INVALID_HANDLE_VALUE (-1). STC JZ .90: ; Abort with CF when INVALID_HANDLE_VALUE. MOV RDI,[%StdOutputString] ; String pointer. MOV RCX,[%StdOutputSize] ; String maximal size. SUB EAX,EAX MOV R8,RDI ; Remember start of string in R8. REPNE SCASB ; Find the end of string. JNE .10: DEC RDI ; Skip the NUL character. .10: SUB RDI,R8 ; RDI is now string size in bytes. PUSH RDX ; Make room for the written-size DWORD. MOV RDX,RSP WinAPI WriteConsoleA,R10,R8,RDI,RDX,0 POP RDX CMP RDX,RDI ; Set CF if not all characters were written. .90: POP R11,R10,R9,R8,RDX,RCX,RAX RET 3*8 ENDP1 StdOutputAC@RT:: %ENDIF AC AF %IF "%StdOutputRT" === "StdOutputAF@RT" StdOutputAF@RT:: PROC1 ; ANSI File variant. PUSHQ RAX,RCX,RDX,R8,R9,R10,R11 %StdOutputHandle %SET RSP+80 %StdOutputSize %SET RSP+72 %StdOutputString %SET RSP+64 MOV RAX,[%StdOutputHandle] WinAPI GetStdHandle,RAX ; Output handle identifier. MOV R10,RAX ; Save the (redirected) handle to R10. INC RAX ; Test on INVALID_HANDLE_VALUE (-1). STC JZ .90: ; Abort with CF when INVALID_HANDLE_VALUE. MOV RDI,[%StdOutputString] ; String pointer. MOV RCX,[%StdOutputSize] ; String maximal size. SUB EAX,EAX MOV R8,RDI ; Remember start of string in R8. REPNE SCASB ; Find the end of string. JNE .10: DEC RDI ; Skip the NUL character. .10: SUB RDI,R8 ; RDI is now string size in bytes. PUSH RDX ; Make room for the written-size DWORD. MOV RDX,RSP WinAPI WriteFile,R10,R8,RDI,RDX,0 POP RDX CMP RDX,RDI ; Set CF if not all characters were written. .90: POP R11,R10,R9,R8,RDX,RCX,RAX RET 3*8 ENDP1 StdOutputAF@RT:: %ENDIF AF %ENDMACRO StdOutput
Macro StdInput reads a line terminated with CR from standard input specified by the Handle identifier.
StdInput %MACRO Buffer, Size=, Handle=-10 %IF "%Size"==="" PUSHQ %Handle, SIZE# %Buffer, %Buffer %ELSE PUSHQ %Handle, %Size, %Buffer %ENDIF CALL StdInput@RT StdInput@RT::PROC1 PUSH RAX,RDX,R8,R9,R10,R11 %StdInputHandle %SET RSP+72 %StdInputSize %SET RSP+64 %StdInputBuffer %SET RSP+56 MOV RAX,[%StdInputHandle] WinAPI GetStdHandle,RAX SUB ECX,ECX ; Clear RCX. MOV R10,RAX ; Save the handle to R10. INC RAX ; Test on INVALID_HANDLE_VALUE (-1). STC JZ .90: ; Abort with CF when RAX=-1 (INVALID_HANDLE_VALUE). MOV R8,[%StdInputBuffer] MOV R9,[%StdInputSize] PUSH RCX ; Make room for the read size. MOV RCX,RSP WinAPI ReadFile,R10,R8,R9,RCX,0 POP RCX ; Number of bytes read. SUB RAX,1 ; Return CF if ReadFile returned FALSE=0. .90:POP R11,R10,R9,R8,RDX,RAX RET 3*8 ENDPROC1 StdInput@RT:: %ENDMACRO StdInput
Errorlevel=
, this value may also be specified as an ordinal operand.
TerminateProgram %MACRO Errorlevel=0 IMPORT ExitProcess %IF %#=1 ; If ordinal provided. PUSHQ %1 %ELSE PUSHQ %Errorlevel ; If keyword provided. %ENDIF CALL ExitProcess:: %ENDMACRO TerminateProgram
ENDHEAD winabi