| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# winabi.htm
Macros
GetArg
GetArgCount
MemAlloc
MemFree
PutArg
PutEnv
StdInput
StdOutput
TerminateProgram
WinABI

MS Windows x64 Application Binary Interface macros.

This library contains macros for some basic OS interactions: retrieving environment and command-line arguments, standard I/O, program termination, invocation of 64bit MS Windows Application Binary Interface as specified in [WinABI64].

Programming interface macros represent elegant way of interaction with operating system using only one single statement. Otherwise we would have to push or load appropriate registers with argument values in the right order, store caller-save registers, manually arrange stack alignment, call the imported function and finally restore the stack.

> Most macro names in this 64bit library winabi.htm are identical with macros from 32bit library winapi.htm and other API libraries. If you really need to include both libraries in one common source file, definitions of those macros should be forgotten before inclusion of the other library with `%DROPMACRO GetArg,GetArgCount,GetEnv,StdInput,StdOutput,TerminateProgram` or with `%DROPMACRO *` in order to prevent warning `W2512 Overwriting macro "!1S" previously defined at !2@`.

    winabi HEAD
           INCLUDEHEAD1 winansi.htm  ; Make sure that %WinANSI is available before WinABI invocation.

↑ WinABI Function, Argument1, Argument2,,,Unicode=%^UNICODE, Lib=, Fastmode=No

  Macroinstruction WinABI invokes Function exported from MS Windows 64bit by [WindowsAPI] in the FastCall calling convention compatible with [WinABI64].

  Ambiguous functions, which have both ANSI and WIDE variant, may be specified with or without explicit suffix `A` or `W`.
  Function may also be provided as a GPR with address of the function (pointer to its thunk in [.idata]).

  The macro is similar to FastCall's Invoke with two differences:

  1. If the Function name occurs on the %WinANSI list, it will be suffixed with `A` or `W`, depending on current %Unicode value.
  2. The Function name will be declared as imported from library `Lib=`.

  WinABI functions require Direction Flag be zero on input and they do not change its value.
  64bit Windows functions do not keep the original contents of flags, RCX, RDX, R8..R11,XMM0..XMM5, but when this macro WinABI is invoked with keyword `Fastmode=No` (default), it preserves all XMML and GP registers except for RAX and XMM0.

  According to FastCall convention, arguments are pushed backwards on stack as QWORDs, starting from the last to the fifth. The first four parameters are loaded to RCX, RDX, R8, R9 and not pushed on stack. Nevertheless, room for the first four arguments is always reserved on stack (so called shadow space) even when the invoked function has less than four arguments.
  When the Function expects argument in floating-point format instead of integer, pointer or immediate, the first four arguments are loaded to XMM0, XMM1, XMM2, XMM3 rather than to GPR.
  Stack pointer will be OWORD aligned before the CALL instruction is performed.

  Invoked function does not remove arguments from stack, it terminates with a simple near RET. Epilogue of macro WinABI restores RSP to its original value.

Input
  Function is the name of invoked WinAPI procedure imported from system DLL
  Function may also be provided in the form of GPR containing the entry address of the function (pointer to its thunk in [.idata] section). Import from DLL by ordinal number is not supported.
  Arg* can be a SIMD register or anything pushable:
  - XMM register with floating-point value in its lower bits, for instance `XMM15`,
  - 64bit GP register with integer value or pointer, for instance `RBX`,
  - segment register `FS` or `GS`,
  - 32bit immediate integer value (it will be sign-extended by CPU), for instance `0` or `-11`,
  - numeric symbol, for instance `GMEM_FIXED`,
  - literal or address symbol, for instance `="Hello, world!"` or `MyCallback:`.
  - 64bit memory variable, for instance `[RBP+32]` or `[MyTable+RSI]` or `[=Q 22.5]#SD`.
  It is programmer's responsibility to provide exactly that many arguments as is specified in WinAPI documentation.
  Any argument may be suffixed with type specificator `#SS` or `#SD` (case insensitive), which signalizes that it represents floating-point value in Scalar Single or Scalar Double precision format, and that it should be therefore passed to the Function in XMM register instead of GPR.
  Suffix is not necessary with XMM register (argument passed in XMM is always assumed to contain floating-point number).
  Suffix is not necessary when Fastmode=No, because the robust version transfers the first four arguments simultaneously in SIMD and GP registers anyway, no matter if they are float or integer.
  Suffix is not necessary in 5th and higher arguments in both modes (they are passed via machine stack regardless of their type).
  Unicode=%^UNICODE is an extended Boolean switch which specifies if ANSI or WIDE variant of Function should be called. By default it copies the option set by `EUROASM UNICODE=`.
  Lib= is the name of dynamic linked library (quoted or unquoted, always without path) which exports the invoked Function. This keyword may be empty or omitted
  - if the library is kernel32.dll, or
  - if the Function is explicitly declared somewhere in the program, e.g. with `IMPORT Function, Lib=user32.dll`, or
  - if the Function is declared in some import library linked to the program. Example of such library is objlib\winapi.lib created by the script dll2lib.htm.
  Fastmode=No is an extended Boolean switch. When it's false or omitted (default), the WinABI macro preserves all registers except for RAX and XMM0 which return the result of Function, similary to other robust macros from EuroAssembler libraries.
  Although Microsoft ABI specifies fastcall convention with floating-point parameters %1..%4 delivered in XMM0, XMM1, XMM2, XMM3, and with parameters %1..%4 of all other types delivered in RCX, RDX, R8, R9, arguments of WinABI in robust mode (Fastmode=No) may be supplied in those registers too in arbitrary order, for instance `WinABI MessageBox,RDX,R8,R8,MB_OK`.
  Fastmode=Yes is the macro expansion mode where Invoke | WinABI does not preserve caller-save registers and it loads the first four arguments directly to `XMM0, XMM1, XMM2, XMM3` when they are floating-point values, and to `RCX, RDX, R8, R9` in all other cases, so the invokation is faster. Registers RCX, RDX, R8, R9, XMM0..XMM3 cannot be used as macro arguments, because they are being overwritten in prologue. Or they should be only used in the exact ABI-specified order, e.g. `WinABI Function, RCX, XMM1, R8, XMM3`. Similary, registers RCX, RDX, R8, R9 cannot be used instead of Function name in fast mode. Use other GPR, e.g. `
  IMPORT WriteConsoleW,Lib=kernel32.dll
  LEA RDI, [WriteConsoleW]
  WinABI Fastmode=Yes, RDI, RBX, Message, SIZE# Message, WrittenChars, 0`
Output
  RAX, XMM0, RFlags are set as returned from the invoked Function.
  RBX,RBP,RSP,RSI,RDI,R12..R15,XMM6..XMM15 are preserved (callee-save registers).
  Other XMM and GPR are preserved only if Fastmode=No.
Depends on
  WinANSI
Documentation
  Windows functions are documented in [WindowsSDK] and in [WindowsAPI].
  Microsoft FastCall convention is described in [WinABI64] and [MSx64Conv].
Example
  Expansion of WinABI invokation of the function CreateFile with seven arguments in both modes: `WinABI CreateFileA, FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0` `| | ; Fast Version. |00000000: | WinABI CreateFileA, FileName, GENERIC_READ, FILE_SHARE_READ, 0, \ |00000000: | OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0, Fastmode=Yes |00000000:54 + PUSH RSP ; Store original stack pointer value (equilibrum). |00000001:40F6C408 + TEST SPL,1000b ; Test RSP OWORD alignment at run-time. |00000005:7506 + JNZ .WinABI1: |00000007:54 + PUSH RSP ; Store and update 2nd copy of original RSP (equilibrum). |00000008:4883042408 + ADDQ [RSP],8 ; Those two instructions aren't executed if RSP was properly aligned. |0000000D: + .WinABI1: |0000000D:6A00 + PUSHQ 0 ; Push 7th argument. |0000000F:6880000000 + PUSHQ FILE_ATTRIBUTE_NORMAL ; Push 6th argument. |00000014:6A03 + PUSHQ OPEN_EXISTING ; Push 5th argument. |00000016:41B900000000 + MOV R9,0 ; Load 4th argument. |0000001C:41B801000000 + MOV R8,FILE_SHARE_READ ; Load 3rd argument. |00000022:BA00000080 + MOV RDX,GENERIC_READ ; Load 2nd argument. |00000027:488D0D(00000000) + LEA RCX,[FileName] ; Load 1st argument. |0000002E:4883EC20 + SUB RSP,4*8 ; Make room for shadow space in fast mode. RSP is OWORD-aligned. |00000032:E8(00000000) + CALL CreateFileA ; Call the imported function. |00000037:488D642438 + LEA RSP,[RSP+7*8] ; Discard transferred arguments, keep RFlags. |0000003C:5C + POP RSP ; Restore RSP to equilibrum from 1st or 2nd copy. |0000003D: | ; WinABI in fast mode occupies 61 bytes of code.` `|00000000: | ; Robust version. |00000000: | WinABI CreateFileA, FileName, GENERIC_READ, FILE_SHARE_READ, 0, \ |00000000: | OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0, Fastmode=No |00000000:6A00 + PUSHQ 0 ; Push 7th argument. |00000002:6880000000 + PUSHQ FILE_ATTRIBUTE_NORMAL ; Push 6th argument |00000007:6A03 + PUSHQ OPEN_EXISTING ; Push 5th argument |00000009:6A00 + PUSHQ 0 ; Push 4th argument |0000000B:6A01 + PUSHQ FILE_SHARE_READ ; Push 3rd argument |0000000D:6800000080 + PUSHQ GENERIC_READ ; Push 2nd argument |00000012:50 + PUSH RAX ; Alloc 1st argument on stack. |00000013:488D05(00000000) + LEA RAX,[FileName] ; Load 1st argument. |0000001A:48870424 + XCHG RAX,[RSP] ; Transfer the pointer without clobbering RAX. |0000001E:6A07 + PUSHQ 7 ; Push the number of arguments. |00000020:488D05(00000000) + LEA RAX,[CreateFileA] ; Load the function address (pointer to its thunk in [.idata]). |00000027:E805000000 + CALL WinABI@RT ; Call the runtime with function in RAX. |0000002C:488D642440 + LEA RSP,[RSP+8*8] ; Restore stack to equilibrum, preserving RFlags. |00000031: | ; WinABI in robust mode occupies 49 bytes of code (plus 190 bytes of runtime code, once per program).`
Remark
  Macro WinABI can be expanded in one of two modes:
  - Fast where the first four arguments are loaded directly to fixed registers according to ABI specification and all scratch registers are destroyed. Arguments %1..%4 are copied to transfer registers with `MOV reg,value`(5 bytes). 64bit pointers are transferred by the help of temporary register with `LEA reg,[address]` using RIP-relative addressing (7 bytes) resolvable at link time.
  - Robust mode uses its own runtime procedure for preservation of scratch registers and allows to use them for passing of arguments in arbitrary order. Small scalar arguments are copied to the transfer registers with `PUSH value ; POP reg` (2+1 bytes).

  Selection of the mode depends on optimisation criterion:

  - If you optimise for speed, use Fastmode=Yes.
  - If you optimise for programmer's convenience, use Fastmode=No.
  - If you optimise for code size, it depends on the number and type of arguments:

  Examples of a few typical OS invocations and their emitted sizes in bytes:
  Example of Windows function invocation | Number of args | WinABI Fastmode=Yes | WinABI Fastmode=No
  --- | --- | --- | ---
  `GetCursor` | 0 | 28 | 19
  `SetArcDirection, [hDC], AD_CLOCKWISE` | 2 | 43 | 36
  `MessageBox, NULL, Text, Caption, MB_OK` | 4 | 53 | 47
  `AngleArc, [hDC], 100, 120, R9, [StartAngle]#SS, XMM14#SS` | 6 | 74 | 65
  `CreateFile, FileName, GENERIC_READ, FILE_SHARE_READ, 0, \ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0` | 7 | 61 | 49

  Robust variant of WinABI is often shorter, but it emits 190 additional bytes of runtime procedure (only once in a program), so it is profitable when we have more than cca twenty Windows invocations in the program. Robust variant may also spare some push/pops because it doesn't change any scratch registers.

  > When you want to switch on the Fastmode for all WinABI invokations, you don't have to append `,Fastmode=Yes` to every invokation of `WinABI` or `Invoke` , if you set preprocessing variable %Fastmode in the beginning of your program: `%Fastmode %SETB On`.

Stack alignment
  Fastcall convention requires that stack-pointer RSP should be OWORD (16 bytes) aligned before the function is called. In fast mode this is provided by pushing RSP once or twice in macro prologue.
  Robust mode postpones the actual function call to its runtime procedure Invoke@RT, so the RSP alignment takes place there.
Stack layout of WinABI and Invoke when Fastmode=Yes
  WinABI Function, Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Fastmode=Yes ┌────────┐ │OrigRSP │ <──Only present if RSP was OWORD unaligned. ├────────┤ │OrigRSP │ ╞════════╡ <══RSP is OWORD aligned here. │ Arg6 │ ├────────┤ │ Arg5 │ ╞════════╡ ┐ │ undef │ │ ├────────┤ │ │ undef │ ├─Shadow space. ╞════════╡ │ │ undef │ │ ├────────┤ │ │ undef │ │ ╞════════╡ ┘ │ return │ └────────┘
Stack layout of WinABI and Invoke when Fastmode=No
  WinABI Function, Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Fastmode=No ┌────────┐ ┐ │ Arg6 │ │ ├────────┤ │ │ Arg5 │ │ ├────────┤ │ │ Arg4 │ │ ├────────┤ │ │ Arg3 │ ├─Frame of Invoke@RT. ├────────┤ │ │ Arg2 │ │ ├────────┤ │ │ Arg1 │ │ ├────────┤ │ │NrOfArg │ │ NrOfArg=6 in this example. ├────────┤ ┘ │RET(@RT)│ ├────────┤ ┐ │ RSI │ │ ├────────┤ │ │ RDI │ │ ├────────┤ │ │ R12 │ │ ├────────┤ │ │ RCX │ │ ├────────┤ │ │ RDX │ │ ├────────┤ │ │ R8 │ │ ├────────┤ │ │ R9 │ │ ├────────┤ │ │ R10 │ │ ├────────┤ │ │ R11 │ ├─Local stack ├────────┤ │ of Invoke@RT. │ XMM1L │ │ ├────────┤ │ │ XMM2L │ │ ├────────┤ │ │ XMM3L │ │ ├────────┤ │ │ XMM4L │ │ ├────────┤ │ │ XMM5L │ │ ├────────┤ │ ┐ │(align) │ │ ├──────Only present if RSP is OWORD-unaligned. ╞════════╡ ┘ ┘ ─┐ │ Arg6 │ │ ├────────┤ │ │ Arg5 │ │ ╞════════╡ ┐ │ │ Arg4 │ │ │ ├────────┤ │ ├─Frame of Function. │ Arg3 │ │ │ ╞════════╡ ├Shadow │ │ Arg2 │ │ space │ ├────────┤ │ │ │ Arg1 │ │ │ ╞════════╡ ┘ ─┘ <══ RSP is OWORD-aligned here. │RET(API)│ └────────┘

    WinABI %MACRO Function, Arg1, Arg2,,, Fastmode=%Fastmode, Unicode=%^UNICODE, Lib=
    %Fast   %SETB %Fastmode
    %Robust %SETB ! %Fast
    Fa      %IF %Fast                       ; Align stack in fast mode only.
              PUSH RSP                      ; Store original stack pointer value (equilibrum).
              TEST SPL,1000b                ; Test stack OWORD alignment at run-time.
    FaEv      %IF %# & 1b || %# <= 5        ;>If the number of Function arguments is 0,1,2,3,4,6,8,10,,(even),
                JZ .WinABI%.:               ;   store 2nd copy of equilibrum when RSP is OWORD-unaligned.
              %ELSE FaEv                    ; If the number of arguments is 5,7,9,11,,, (odd),
                JNZ .WinABI%.:              ;   store 2nd copy of equilibrum when RSP is OWORD-aligned.
              %ENDIF FaEv
              PUSH RSP                      ; Store and update 2nd copy of original RSP (equilibrum).
              ADDQ [RSP],8                  ; Those two instructions aren't executed if RSP was properly aligned.
    .WinABI%.:
            %ENDIF Fa
    %GPR    %SET  RCX,RDX,R8,R9             ; Enumerate registers for transfer of integer|pointer values.
    %SIMD   %SET  XMM0,XMM1,XMM2,XMM3       ; Enumerate registers for transfer of floating-point values.
    %ArgNr  %SETA %#                        ; Number of macro ordinals, i.e. number of Function arguments + 1.
    Arg     %WHILE %ArgNr > 1
    %Arg     %SET %*{%ArgNr}                ; Transfer all Function arguments, start with the last one.
    %ArgNr   %SETA %ArgNr-1                 ; %ArgNr is now the ordinal Nr of Function argument (,,3,2,1).
    %suffix  %SET Q                         ; %suffix of MOV will be Q, SS or SD (MOVQ, MOVSS or MOVSD).
             %IF '%Arg[%&-2..%&-1]'=='#S'   ; If suffix #SS or #SD is present in argument notation,
    %suffix    %SET %Arg[%&-1..%&]          ;  let %suffix be SS or SD
    %Arg       %SET %Arg[1..%&-3]           ;  and remove it from the argument.
             %ENDIF                         ; %Arg may be GPR,SIMD,imm@abs,ptr@rel,[mem@abs],[mem@rel].
    Rb       %IF %ArgNr>4 || %Robust        ; Transfer %Arg via stack.
    RbSc       %IF TYPE#(SEGMENT#(%Arg))='N'; %Arg is not relocatable (scalar).
    RbScRg       %IF TYPE#(%Arg)='R'        ; It can be GPR,SIMD,imm@abs,[mem@abs].
    RbScRgXm       %IF REGTYPE#(%Arg)='X'   ; %Arg is a GP or SIMD register.
                     SUB RSP,8
                     MOV%suffix [RSP],%Arg  ; %Arg is a SIMD register.
                   %ELSE RbScRgXm
                     PUSHQ %Arg             ; %Arg is a GP register.
                   %ENDIF RbScRgXm
                 %ELSE RbScRg               ; %Arg it not a register.
                   PUSHQ %Arg               ; %Arg is scalar immediate or [mem], e.g. 1 or [RBP+16].
                 %ENDIF RbScRg
               %ELSE RbSc                   ; %Arg is relocatable (vector), e.g. Symbol or [Symbol+RSI].
    RbVeM        %IF '%Arg[1]' === '['      ; Argument is passed by value, via a temporary GPR.
                   PUSH RAX                 ; Original contents of the borrowed RAX must be kept.
                   LEA RAX,%Arg             ; Use relative addressing frame for relocatable %Arg.
                   MOV RAX,[RAX]            ; Dereference the argument value.
                   XCHG RAX,[RSP]           ; Transfer the value.
                 %ELSE RbVeM                ; Relocatable argument is passed by reference, e.g. Symbol.
                   PUSH RAX                 ; Original contents of the borrowed RAX must be kept.
                   LEA RAX,[%Arg]           ; Use relative addressing frame for relocatable %Arg.
                   XCHG RAX,[RSP]           ; Transfer the pointer.
                 %ENDIF RbVeM
               %ENDIF RbSc
             %ELSE Rb                       ; Fastmode=Yes and %ArgNr=4,3,2,1. Transfer via registers.
    FaSc       %IF TYPE#(SEGMENT#(%Arg))='N'; %Arg is not relocatable (scalar).
    FaScRg       %IF TYPE#(%Arg)='R'        ; It can be GPR,SIMD,imm@abs,[mem@abs].
    FaScRgXm       %IF REGTYPE#(%Arg)='X'   ; %Arg is a GP or SIMD register.
                     %IF "%SIMD{%ArgNr}" !== "%Arg"  ; %Arg is XMM. Skip when it's already there.
                       MOV%suffix %SIMD{%ArgNr},%Arg ; Copy SIMD %Arg to other SIMD (XMM0..XMM3).
                     %ENDIF
                   %ELSE FaScRgXm           ; %Arg is GPR.
                     %IF "%GPR{%ArgNr}" !== "%Arg"   ; Skip when it's already there.
                        MOV %GPR{%ArgNr},%Arg        ; Copy GPR to other GPR (R9,R8,RDX,RCX).
                     %ENDIF
                   %ENDIF FaScRgXm
                 %ELSE FaScRg               ; %Arg is scalar immediate or [mem], e.g. 1 or [RBP+16].
    FaScIm         %IF '%suffix' === 'Q'
                     MOV %GPR{%ArgNr},%Arg  ; %Arg is integer value.
                   %ELSE FaScIm
                     MOV%suffix %SIMD{%ArgNr},%Arg ; %Arg is FP value, e g. [RSI].
                   %ENDIF FaScIm
                 %ENDIF FaScRg
               %ELSE FaSc                   ; %Arg is relocatable (vector), e.g. Symbol or [Symbol+RSI].
    FaVeM        %IF '%Arg[1]' === '['      ; Argument is passed by value, via this GPR.
                   LEA %GPR{%ArgNr},%Arg
                   MOV %GPR{%ArgNr},[%GPR{%ArgNr}] ; Dereference the argument value, transfer the value.
                 %ELSE FaVeM                ; Argument is passed by reference, e.g. Symbol.
                   LEA %GPR{%ArgNr},[%Arg]  ; Transfer the pointer.
                 %ENDIF FaVeM
               %ENDIF FaSc
             %ENDIF Rb
            %ENDWHILE Arg                   ; All arguments are loaded|pushed.
    %sfx    %SET                            ; A or W Function suffix. Empty by default.
    reg     %IF REGTYPE# %Function = 'Q'    ; Function was specified as 64bit GPR.
              %IF "%Function"=="RCX"||"%Function"=="RDX"||"%Function"=="R8"||"%Function"=="R9"
                %ERROR ID=5956,'%0 function cannot be supplied in scratch register %Function when Fastmode=Yes.'
                %EXITMACRO Invoke
              %ENDIF
            %ELSE reg                       ; Function was specified by name.
    fn        %FOR %WinANSI
    found       %IF '%fn' === '%Function'
                  %IF %Unicode
    %sfx            %SETC 'W'
                  %ELSE
    %sfx            %SETC 'A'
                  %ENDIF
                  %EXITFOR fn               ; No need for further examination of the list.
                %ENDIF found
              %ENDFOR fn                    ; %sfx is now A or W or empty.
              IMPORT %Function%sfx, Lib=%Lib
            %ENDIF reg
    Fa      %IF %Fast
              SUB RSP,4*8                   ; Make room for shadow space in fast mode. RSP is OWORD-aligned.
              CALL %Function%sfx            ; Call the function in fast mode.
              %IF %# > 5
                LEA RSP,[RSP+8*(%#-1)]      ; Discard transferred arguments, keep RFlags.
              %ELSE
                LEA RSP,[RSP+8*4]           ; Discard transferred arguments, keep RFlags.
              %ENDIF
              POP RSP                       ; Restore RSP to equilibrum from 1st or 2nd copy.
            %ELSE Fa                        ; In robust mode use helper runtime procedure WinABI@RT.
              PUSHQ %#-1                    ; Push the number of Function arguments.
    RbRg      %IF REGTYPE# %Function = 'Q'  ; Function was specified as 64bit GPR.
                %IF '%Function' !== 'RAX'   ; Skip if it's already in RAX.
                  MOV RAX,%Function
                %ENDIF
              %ELSE RbRg
                LEA RAX,[%Function%sfx]     ; RVA of the Function (pointer to its thunk in [.idata]).
              %ENDIF RbRg
              CALL WinABI@RT                ; Arguments+their number are on stack. Function is in RAX.
              LEA RSP,[RSP + 8 * %#]        ; Restore stack to equilibrum, preserving RFlags.
    WinABI@RT: PROC1                        ; Macro emits the runtime subroutine, which is expanded only once in program.
                PUSH RSI,RDI,R12            ; Calee-save registers used by WinABI@RT procedure.
                PUSH RCX,RDX,R8,R9,R10,R11  ; Robust version preserves scratch GP registers.
                MOVQ RDX,XMM1
                MOVQ  R8,XMM2
                MOVQ  R9,XMM3
                MOVQ R10,XMM4
                MOVQ R11,XMM5
                PUSH RDX,R8,R9,R10,R11      ; Robust version preserves scratch SIMD registers.
                MOV ECX,[RSP+15*8]          ; Number of Function arguments.
                LEA RSI,[RSP+16*8]          ; Pointer to %Arg1 in WinABI@RT stack frame.
                CMP ECX,4                   ; Number of arguments is 0|1|2|3|4|5|6...
                JAE .AtLeast4:
                MOV CL,4                    ; Number is saturated to 4|4|4|4|4|5|6...
      .AtLeast4:MOV R12,RSP                 ; Save (perhaps unaligned) RSP to callee-preserved register.
                ; Align stack pointer as dictated by ABI specification.
                MOV EDX,ECX                 ; Saturated number of operands (4,5,6,,,).
                SHL EDX,3                   ; RDX bit 3 is set if saturated number of arguments is odd (5,7,9,,).
                XOR EDX,ESP                 ; RDX will be 8 when number of arguments is even and RSP is OWORD unaligned,
                AND EDX,0000_1000b          ;   or when it's odd and RSP is already OWORD aligned. Otherwise RDX=0.
                SUB RSP,RDX                 ; Align RSP to OWORD.
                ; Copy ECX arguments from RSI to the callee's shadow space.
                MOV EDX,ECX
                SHL EDX,3
                SUB RSP,RDX                 ; Alloc Function frame (shadow space + possible other arguments).
                MOV RDI,RSP                 ; RSP is OWORD aligned at this moment.
                REP MOVSQ                   ; Copy all pushed arguments.
                ; Load first four arguments to GP registers.
                MOVQ RCX,[RSP+00]
                MOVQ RDX,[RSP+08]
                MOVQ R8, [RSP+16]
                MOVQ R9, [RSP+24]
                ; Copy first four arguments to SIMD registers (for the case they were floating-point).
                MOVQ XMM0,RCX
                MOVQ XMM1,RDX
                MOVQ XMM2,R8
                MOVQ XMM3,R9
                CALL RAX                    ; Call the Function in robust mode.
                MOV RSP,R12                 ; Discard Function frame plus possible stack-alignment stuff.
                POP R11,R10,R9,R8,RDX
                MOVQ XMM5,R11
                MOVQ XMM4,R10
                MOVQ XMM3,R9
                MOVQ XMM2,R8
                MOVQ XMM1,RDX               ; Restore SIMD scratch registers.
                POP R11,R10,R9,R8,RDX,RCX   ; Restore GP scratch registers.
                POP R12,RDI,RSI             ; Restore used callee-save registers.
                RET
               ENDP1 WinABI@RT:
            %ENDIF  Fa
          %ENDMACRO WinABI

↑ MemAlloc Size
  Macro MemAlloc allocates memory from Windows.
Input
  Size is the size of allocated memory in bytes. It will be rounded up to the multiply of pagesize (64 KiB).
Output
  CF=0,
  RAX is pointer to the first byte of the allocated memory.
Error
  CF=1
  RAX=0
Depends on
  WinABI
See also
  MemFree

    MemAlloc %MACRO Size
         WinABI VirtualAlloc, 0, %Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, Fastmode=no
         CMP RAX,1
        %ENDMACRO MemAlloc

↑ MemFree Address
  Macro MemFree unallocates memory from Windows.
Input
  Address as returned from MemAlloc which allocated it.
Output
  CF=0,
  RAX=0
Error
  CF=1
  RAX > 0
Depends on
  WinABI
See also
  MemAlloc

    MemFree %MACRO Address, Size
         WinABI VirtualFree, %Address, 0, MEM_RELEASE, Fastmode=no
         CMP RAX,1
        %ENDMACRO MemFree

↑ GetArg ArgNumber, Unicode=%^UNICODE

  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 apostrophe cannot be used as quote.
  Macro returns 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.

Input
  ArgNumber (64bit register or memory or immediate number) is ordinal number of the required parameter. The 0-th parameter is the executable file name itself.
  Unicode=%^UNICODE is Boolean keyword specifying if the returned string should be ANSI or WIDE.
Output
  CF=0,
  RSI is pointer to the first character of argument,
  RCX is the size of argument in bytes.
Error
  CF=1 if odd number of quotes or if requested argument was not provided.
  RSI=RCX=0
Depends on
  WinABI
Tested by
  twinabi.htm
Example
  GetArg 1 ; Assume that our program should specify a filename. JC .BadArgument: ; Report error if no file was provided. StripQuotes RSI,RCX ; Get rid of quotes if they were used. MOV RDI,InputFileName$ ; Room for the filename. REP MOVSB ; Copy the filename. SUB AL,AL STOSB ; Zero terminate the string.

    GetArg %MACRO ArgNumber, Unicode=%^UNICODE
              PUSHQ %ArgNumber
    AorW      %IF %Unicode
                 CALL GetArgWin64W@RT   ; WIDE variant.
    GetArgWin64W@RT: PROC1
        PUSHQ RAX,RDX,RSI,RDI,R8,R9,R10,R11
        WinABI GetCommandLineW, Fastmode=Yes
        MOV RSI,RAX      ; RSI = parser pointer.
        MOV RDI,RAX      ; RDI = end of current Arg.
        MOV R8,RAX       ; R8  = begin of current Arg.
        XOR ECX,ECX      ; RCX = current Arg ordinal.
        MOV R10,[RSP+72] ; R10 = requested Arg ordinal.
        MOV [RSP+40],RCX ; %ReturnRSI.
        DEC RCX
        CMP  R10,RCX     ; Requested ordinal is -1 when GetArg is invoked from GetArgCount.
        ADC  R10,0       ; Correction for the 0-th string on cmd-line.
        XOR EAX,EAX
        CMP EAX,ESI
        STC
        JZ .90:           ; If GetCommandLine returned FALSE.
        REPNE SCASW
        LEA RDX,[RDI-2]   ; RDX = end of command line.
        XOR ECX,ECX       ; RCX = current Arg ordinal.
    .10:MOV RDI,RSI       ; RDI = brutto end of current argument.
        CMP RCX,R10
        JAE .50:          ; Jump if requested ordinal was just found.
    .20:CMP RSI,RDX
        JNB .50:          ; If no more arguments available.
        LODSW
        CMP AX,' '
        JBE .20:
        SUB RSI,2
        INC RCX
        MOV R8,RSI       ; R8 is brutto beginning of RCX-th argument.
        MOV RDI,RSI
    .30:CMP RSI,RDX
        JNB .10:
        LODSW
        CMP AX,'"'
        JNE .40:
    .35:CMP RSI,RDX      ; Inside quotes look for ending quote.
        CMC
        JC .90:          ; Syntax error - unpaired quotes.
        LODSW
        CMP AX,'"'
        JNE .35:
        JMP .30:
    .40:CMP AX,','
        JE .10:
        CMP AX,' '
        JA .30:
    .45:CMP RSI,RDX
        JNB .10:
        LODSW
        CMP AX,' '
        JNA .45:
        CMP AX,','
        JE .10:
        SUB RSI,2
        JMP .10:
    .50:CMP  R10,-1       ; Test if invoked from GetArgCount with ArgNr= -1.
        JNE .60:
        DEC RCX           ; Omit the 0-th argument (the executable itself).
        JMP .90:
    .60:CMP RCX,R10
        MOV RCX,0
        STC
        JNE .90:          ; Return with CF and RCX=0 when the requested Arg is not provided.
        MOV RSI,R8        ; Brutto RCX-th argument found at RSI..RDI. Trim spaces and commas.
    .65:CMP RSI,RDI
        JNB .75:
        LODSW
        CMP AX,' '
        JBE .65:
        CMP AX,','
        JE .65:
        SUB RSI,2
    .70:SUB RDI,2
        MOV AX,[RDI]
        CMP AX,' '
        JBE .70:
        CMP AX,','
        JE .70:
        ADD RDI,2         ; Netto argument is now at RSI..RDI.
    .75 MOV [RSP+40],RSI  ; %ReturnRSI.
        SUB RDI,RSI
        JC .80:
        MOV RCX,RDI
    .80:CLC
    .90:POPQ R11,R10,R9,R8,RDI,RSI,RDX,RAX
        RET 1*8
      ENDP1 GetArgWin64W@RT
         %ELSE AorW
           CALL GetArgWin64A@RT
    GetArgWin64A@RT: PROC1 ; Stdcalled with %Param1=ArgNumber.
        PUSHQ RAX,RDX,RSI,RDI,R8,R9,R10,R11
        WinABI GetCommandLineA, Fastmode=Yes
        MOV RSI,RAX      ; RSI = parser pointer.
        MOV RDI,RAX      ; RDI = end of current Arg.
        MOV R8,RAX       ; R8  = begin of current Arg.
        XOR ECX,ECX      ; RCX = current Arg ordinal.
        MOV R10,[RSP+72] ; R10 = requested Arg ordinal.
        MOV [RSP+40],RCX ; %ReturnRSI.
        DEC RCX
        CMP R10,RCX      ; Requested ordinal is -1 when GetArg is invoked from GetArgCount.
        ADC R10,0        ; Correction for the 0-th string on cmd-line.
        XOR EAX,EAX
        CMP EAX,ESI
        STC
        JZ .90:           ; If GetCommandLine returned FALSE.
        REPNE SCASB
        LEA RDX,[RDI-1]   ; RDX = end of command line.
        XOR ECX,ECX       ; RCX = current Arg ordinal.
    .10:MOV RDI,RSI       ; RDI = brutto end of current argument.
        CMP RCX,R10
        JAE .50:          ; Jump if requested ordinal was just found.
    .20:CMP RSI,RDX
        JNB .50:          ; If no more arguments available.
        LODSB
        CMP AL,' '
        JBE .20:
        DEC RSI
        INC RCX
        MOV R8,RSI        ; R8 is brutto beginning of RCX-th argument.
        MOV RDI,RSI
    .30:CMP RSI,RDX
        JNB .10:
        LODSB
        CMP AL,'"'
        JNE .40:
    .35:CMP RSI,RDX       ; Inside quotes look for ending quote.
        CMC
        JC .90:           ; Syntax error - unpaired quotes.
        LODSB
        CMP AL,'"'
        JNE .35:
        JMP .30:
    .40:CMP AL,','
        JE .10:
        CMP AL,' '
        JA .30:
    .45:CMP RSI,RDX
        JNB .10:
        LODSB
        CMP AL,' '
        JNA .45:
        CMP AL,','
        JE .10:
        DEC RSI
        JMP .10:
    .50:CMP R10,-1        ; Test if invoked from GetArgCount with ArgNr= -1.
        JNE .60:
        DEC RCX           ; Omit the 0-th argument (the executable itself).
        JMP .90:
    .60:CMP RCX,R10
        MOV RCX,0
        STC
        JNE .90:          ; Return with CF and RCX=0 when the requested Arg is not provided.
        MOV RSI,R8        ; Brutto RCX-th argument found at RSI..RDI. Trim spaces and commas.
    .65:CMP RSI,RDI
        JNB .75:
        LODSB
        CMP AL,' '
        JBE .65:
        CMP AL,','
        JE .65:
        DEC RSI
    .70:DEC RDI
        MOV AL,[RDI]
        CMP AL,' '
        JBE .70:
        CMP AL,','
        JE .70:
        INC RDI           ; Netto argument is now at RSI..RDI.
    .75 MOV [RSP+40],RSI  ; %ReturnRSI.
        SUB RDI,RSI
        JC .80:
        MOV RCX,RDI
    .80:CLC
    .90:POPQ R11,R10,R9,R8,RDI,RSI,RDX,RAX
        RET 8
      ENDP1 GetArgWin64A@RT
         %ENDIF AorW
    %ENDMACRO GetArg

↑ GetArgCount
  counts arguments provided on the command line of the executed program. Arguments may be separated with unquoted spaces or commas. Multiple white spaces are treated like a single space. Comma-separated empty arguments are counted, too. Single apostrophe cannot be used as a quote.
Input
  is taken from the command line which launched the program.
Output
  CF=0
  RCX=number of arguments on the command line which launched the program.
Error
  CF=1 if odd number of quotes detected.
Depends on
  GetArg
Tested by
  twinabi.htm
Example
  All examples of the command lines below will return RCX=4.
  Program.exe arg1 arg2 arg3 arg4 Program.exe arg1,arg2, ,arg4 Program.exe , , , , , Program.exe arg1, "arg2,arg2" arg3 arg4

    GetArgCount %MACRO
           GetArg -1, Unicode=%^UNICODE
         %ENDMACRO GetArgCount

↑ PutArg ArgNumber, BufPtr, BufSize, IgnoreCase=Yes, Unicode=%^UNICODE

  Macro PutArg retrieves ArgNumber-th argument from command-line and copies its content to the memory specified by BufPtr, BufSize,, including the NUL character at its end.

Input
  ArgNumber is ordinal number of the required parameter. The 0-th parameter specifies the executable file itself. ArgNr can be immediate number, 64bit GPR or memory variable.
  BufPtr is pointer to the caller-reserved memory for the argument value.
  BufSize is size of the reserved memory in bytes. This 3rd parameter may be omitted, `SIZE# %BufPtr` will be used instead.
  Unicode=%^UNICODE is extended Boolean value specifying if the string written to BufPtr should be ANSI or WIDE. By default it copies the value specified by `EUROASM UNICODE=`.

  > When the argument value expects e.g. not more than 10 characters and `UNICODE=Yes`, you should reserve BufSize at least 22 bytes (10+1 WIDE characters).
  > In €ASM you can define the output buffer as `OutBuffer D (11 >> %^UNICODE) * BYTE` , which will assemble as `OutBuffer D 11 * BYTE` when `UNICODE=0` and as `OutBuffer D (11 << 1) * BYTE` when `UNICODE= -1`, always reserving room for 10+1 characters.

Output
  CF=0 if the requested ASCIIZ value was successfully copied to Buffer.
  RCX=number of bytes written to BufPtr, without the terminating NUL character.
  RCX=0 when the ArgNumber-th argument does not exist.
Error
  CF=1 if the value size overflowed BufSize and is therefore incomplete and not zero-terminated.
  RCX=number of bytes written to BufPtr (never above BufSize).
Tested by
  twinabi.htm
Depends on
  GetArg
Example
  ; This example works with both ANSI and WIDE strings. EUROASM UNICODE=On ; or UNICODE=Off, no further intervention is necessary. [.bss] FileName$ DB (260 >> %^UNICODE) * BYTE ; Reserve 260 or 520 bytes. [.text] PutArg 1,FileName$

    PutArg %MACRO ArgNumber, BufPtr, BufSize, Unicode=%^UNICODE
    sized?  %IF %# = 2
    mem?      %IF %^PASS > 1 && TYPE#(SEGMENT# %BufPtr) != 'A'
                %ERROR ID=5814, 'Please specify the size of output buffer.'
                %EXITMACRO PutArg
              %ELSE mem?  ; BufPtr is specified as a memory variable with size.
    %PutArgSize %SETA SIZE# %BufPtr
               %ENDIF mem?
            %ELSE sized?  ; BufSize is explicitly specified.
    %PutArgSize %SET %BufSize
            %ENDIF sized?
            PUSH RDX,RSI,RDI
              %IF "%BufPtr[1]"==="["
                MOV RDI,%BufPtr
              %ELSE
                %IF "%BufPtr" !== "RDI"
                  LEA RDI,[%BufPtr]
                %ENDIF
              %ENDIF
              XOR EDX,EDX
              %IF %Unicode
                MOV [RDI],DL
              %ELSE
                MOV [RDI],DX
              %ENDIF
              GetArg %ArgNumber, Unicode=%^Unicode
              JC PutArgY%.:
              MOV EDX,%PutArgSize
              INC ECX
              %IF %Unicode
                INC ECX
              %ENDIF
              CMP EDX,ECX
              JAE PutArgX%.:
              MOV ECX,EDX
              REP MOVSB
              JMP PutArgY%.:
    PutArgX%.:DEC ECX
              %IF %Unicode
                DEC ECX
                MOV EDX,ECX
                REP MOVSB
                MOV [EDI],CX
              %ELSE
                MOV EDX,ECX
                REP MOVSB
                MOV [EDI],CL
              %ENDIF
    PutArgY%.:MOV ECX,EDX
             POP RDI,RSI,RDX
           %ENDMACRO PutArg

↑ PutEnv EnvName$, BufPtr, BufSize, IgnoreCase=Yes, Unicode=%^UNICODE

  Macro PutEnv retrieves environment-variable with zero-terminated name EnvName$ at run-time, and copies its content to the memory specified by BufPtr, BufSize,, including the NUL character at its end.

Input
  EnvName$ is pointer to zero-terminated string with environment-variable name (ANSI or WIDE).
  BufPtr is pointer to the caller-reserved memory for the variable value.
  BufSize is size of the reserved memory in bytes. This 3rd parameter may be omitted, `SIZE# %BufPtr` will be used instead.
  IgnoreCase=Yes is extended Boolean value specifying if PutEnv should search for EnvName$ case-insensitively.
  Unicode=%^UNICODE is extended Boolean value specifying if EnvName$ and the string written to BufPtr are both ANSI or WIDE. By default it copies the value specified by `EUROASM UNICODE=`.

  > When the environment value expects e.g. not more than 10 characters and `UNICODE=Yes`, you should reserve BufSize at least 22 bytes (10+1 WIDE characters).
  > In €ASM you can define the output buffer as `OutBuffer D (11 >> %^UNICODE) * BYTE` , which will assemble as `OutBuffer D 11 * BYTE` when `UNICODE=0` and as `OutBuffer D (11 << 1) * BYTE` when `UNICODE= -1`, always reserving room for 10+1 characters.

Output
  CF=0 if the requested ASCIIZ value was successfully copied to Buffer.
  RCX=number of bytes written to BufPtr, without the terminating NUL character.
  RCX=0 when the environment variable is empty or does not exist.
Error
  CF=1 if the value size overflowed BufSize and is therefore incomplete.
  RCX=number of bytes written to BufPtr (never above BufSize).
Tested by
  twinabi.htm
Example
  ; This example works with both ANSI and WIDE strings. EUROASM UNICODE=On ; or UNICODE=Off, no further intervention is necessary. [.bss] TempDir$ DB (260 >> %^UNICODE) * BYTE ; Reserve 260 or 520 bytes. [.text] PutEnv ="TEMP",TempDir$ ; Retrieve TEMP to TempDir$. LEA RDI,[TempDir$ + RCX] MOV RSI, ="\Cache.tmp" ; Netto 10 characters. MOV ECX, (10+1) >> %^UNICODE ; Size of the literal ="\Cache.tmp" in bytes. REP MOVSB ; Append the cache file name to TempDir$ name. ; TempDir$ now contains WIDE or ANSI string e.g. "C:\TMP\Cache.tmp",0.

    PutEnv %MACRO EnvName$, BufPtr, BufSize, IgnoreCase=Yes, Unicode=%^UNICODE
    sized?  %IF %# = 2
    mem?      %IF %^PASS > 1 && TYPE#(SEGMENT# %BufPtr) != 'A'
                %ERROR ID=5814, 'Please specify the size of output buffer.'
                %EXITMACRO PutEnv
              %ELSE mem?  ; BufPtr is specified as a memory variable with size.
    %PutEnvSize %SETA SIZE# %BufPtr
               %ENDIF mem?
            %ELSE sized?  ; BufSize is explicitly specified.
    %PutEnvSize %SET %BufSize
            %ENDIF sized?
    %PutEnvCase %SETB %IgnoreCase
            PUSHQ %PutEnvCase, %PutEnvSize, %BufPtr, %EnvName$
    ansi?   %IF %Unicode
              CALL PutEnvWin64W@RT:
    PutEnvWin64W@RT:PROC1
            XOR ECX,ECX
            PUSH RAX,RBX,RCX,RDX,RBP,RSI,RDI,R8
             MOV RBP,RSP
             XOR EAX,EAX
             MOV RDX,[RBP+10*8]   ; BufPtr.
             MOV RDI,[RSP+9*8]    ; EnvName$.
             MOV [RDX],AX         ; Prepare for the case when EnvName$ not found.
             NOT RCX
             MOV RSI,RDI
             REPNE SCASW
             NOT RCX
             MOV EDX,ECX          ; RDX=size of EnvName$ in unichars including the terminating NUL.
             LEA EDI,[2*RCX+7]
             AND EDI,-8           ; Round RDI up to DWORD.
             SUB RSP,RDI          ; Room for lowercase EnvName$.
             MOV RDI,RSP
    .10:     LODSW
             CMP AX,'A'
             JB .20:
             CMP AX,'Z'
             JA .20:
             OR AL,'x'^'X'        ; Convert character to lowercase.
    .20:     STOSW
             DEC ECX
             JNZ .10:
             WinABI GetEnvironmentStringsW,Fastmode=No
             MOV R8,RAX           ; Environment block.
             MOV RBX,RAX
    .30:     MOV RDI,RBX
             XOR ECX,ECX
             XOR EAX,EAX
             NOT RCX
             MOV RSI,RDI
             CMP [RDI],AX         ; Test the end of environment block.
             JZ .90:              ; Return with RCX=0, CF=0, ZF=1.
             REPNE SCASW
             MOV RBX,RDI
             NOT RCX              ; RSI..RDI is ASCIIZ string EnvName=EnvVal, brutto size in unichars=RCX.
             MOV RDI,RSI
             MOV AX,'='
             REPNE SCASW
             JNE .30:
             SUB RDI,RSI
             SHR EDI,1
             CMP EDI,EDX
             JNE .30:
             LEA ECX,[EDI-1]      ; RCX is unichar-size of netto string EnvName.
             TESTB [RBP+12*8],1   ; PutEnvCase.
             JNZ .40:
             MOV RDI,[RBP+9*8]    ; EnvName$ - case sensitive search.
             REP CMPSW
             JNE .30:
             JMP .70:
    .40:     MOV RDI,RSP          ; Case insensitive search.
    .50:     LODSW
             CMP AX,'A'
             JB .60:
             CMP AX,'Z'
             JA .60:
             OR AL,'x'^'X'        ; Convert character to lowercase.
    .60:     CMP AX,[RDI]
             JNE .30:
             ADD RDI,2
             DEC ECX
             JNZ .50:
    .70:     ADD RSI,2            ; EnvName was found. RSI is now ASCIIZ EnvVal.
             MOV RDI,RSI
             XOR ECX,ECX
             XOR EAX,EAX
             NOT RCX
             REPNE SCASW
             SUB RDI,RSI          ; EDI= required-by-value brutto size of EnvVal in bytes.
             SUB EDI,2
             MOV [RBP+5*8],RDI    ; Return RCX=netto size of EnvVal in bytes.
             ADD RDI,2
             MOV RCX,[RBP+11*8]   ; BufSize. EnvVal allocated-by-user brutto size in bytes.
             CMP ECX,EDI
             XCHG ECX,EDI
             JAE .80:
             XCHG EDI,ECX
    .80:     MOV RDI,[RBP+10*8]    ; BufPtr.
              REP MOVSB
    .90:      PUSHFQ
               WinABI FreeEnvironmentStringsW, R8, Fastmode=No
              POPFQ
              MOV RSP,RBP
             POP R8,RDI,RSI,RBP,RDX,RCX,RBX,RAX
             RET 4*8
            ENDP1 PutEnvWin64W@RT:
           %ELSE ansi?
              CALL PutEnvWin64A@RT:
    PutEnvWin64A@RT: PROC1
            XOR ECX,ECX
            PUSH RAX,RBX,RCX,RDX,RBP,RSI,RDI,R8
             MOV RBP,RSP
             XOR EAX,EAX
             MOV RDX,[RBP+10*8]    ; BufPtr.
             MOV RDI,[RSP+9*8]    ; EnvName$.
             MOV [RDX],AL         ; Prepare for the case when EnvName$ not found.
             NOT RCX
             MOV RSI,RDI
             REPNE SCASB
             NOT RCX
             MOV EDX,ECX          ; RDX=size of EnvName$ in bytes including the terminating NUL.
             LEA EDI,[2*RCX+7]
             AND EDI,-8           ; Round RDI up to QWORD.
             SUB RSP,RDI          ; Room for lowercase EnvName$.
             MOV RDI,RSP
    .10:     LODSB
             CMP AL,'A'
             JB .20:
             CMP AL,'Z'
             JA .20:
             OR AL,'x'^'X'        ; Convert character to lowercase.
    .20:     STOSB
             DEC ECX
             JNZ .10:
             WinABI GetEnvironmentStringsA,Fastmode=No
             MOV R8,RAX           ; Environment block.
             MOV RBX,RAX
    .30:     MOV RDI,RBX
             XOR ECX,ECX
             XOR EAX,EAX
             NOT RCX
             MOV RSI,RDI
             CMP [RDI],AL         ; Test the end of environment block.
             JZ .90:              ; Return with RCX=0, CF=0, ZF=1.
             REPNE SCASB
             MOV RBX,RDI
             NOT RCX              ; RSI..RDI is ASCIIZ string EnvName=EnvVal, brutto size in bytes=RCX.
             MOV RDI,RSI
             MOV AL,'='
             REPNE SCASB
             JNE .30:
             SUB RDI,RSI
             CMP EDI,EDX
             JNE .30:
             LEA ECX,[EDI-1]      ; RCX is the size of netto string EnvName$.
             TESTB [RBP+12*8],1   ; PutEnvCase.
             JNZ .40:
             MOV RDI,[RBP+9*8]    ; EnvName$ - case sensitive search.
             REP CMPSB
             JNE .30:
             JMP .70:
    .40:     MOV RDI,RSP          ; Case insensitive search.
    .50:     LODSB
             CMP AL,'A'
             JB .60:
             CMP AL,'Z'
             JA .60:
             OR AL,'x'^'X'        ; Convert character to lowercase.
    .60:     CMP AL,[RDI]
             JNE .30:
             INC RDI
             DEC ECX
             JNZ .50:
    .70:     INC RSI              ; EnvName was found. RSI is now ASCIIZ EnvVal.
             MOV RDI,RSI
             XOR ECX,ECX
             XOR EAX,EAX
             NOT RCX
             REPNE SCASB
             SUB RDI,RSI          ; RDI= required-by-value brutto size of EnvVal in bytes.
             DEC RDI
             MOV [RBP+5*8],RDI    ; Return RCX=netto size of EnvVal in bytes.
             INC RDI
             MOV RCX,[RBP+11*8]   ; BufSize. EnvVal allocated-by-user brutto size in bytes.
             CMP ECX,EDI
             XCHG ECX,EDI
             JAE .80:
             XCHG EDI,ECX
    .80:     MOV RDI,[RBP+10*8]   ; BufPtr.
              REP MOVSB
    .90:      PUSHFQ
               WinABI FreeEnvironmentStringsA, R8, Fastmode=No
              POPFQ
              MOV RSP,RBP
             POP R8,RDI,RSI,RBP,RDX,RCX,RBX,RAX
             RET 4*8
            ENDP1 PutEnvWin64A@RT:
          %ENDIF ansi?
       %ENDMACRO PutEnv

↑ StdOutput String1, String2,,, Size=-1, Handle=-11, Eol=No, Console=No, Unicode=%^UNICODE

  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 NULL character is never written.

  If keyword Eol=Yes, macro writes CR+LF after all strings.

  > One of four possible runtime subprocedures is selected to emit, depending on the chosen ANSI/WIDE and File/Console options.

Input
  String* is pointer to ANSI or WIDE string.
  Size=-1 is the maximal possible string size in bytes. If its left to -1 (default), the strings must be zero-terminated. This parameter applies to all ordinal operads.
  Handle=-11 is the Windows standard handle identifier. Possible output values are defined in winscon.htm:
  - STD_OUTPUT_HANDLE = -11
  - STD_ERROR_HANDLE = -12
  Eol=No. If Yes, two additional characters CR and LF will be written on output after all strings have been written.
  Console=No (or Yes) is boolean specification whether the macro should use WinABI function WriteFile or WriteConsole.

  > 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.
  > 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.

  > When you want to use the Console (nondefault) mode in all StdOutput and StdInput invokations, you don't have to append `,Console=Yes` to every invokation of `StdOutput` and `StdInput` if you set preprocessing %variable in the beginning of your program: `%StdConsole %SETB Yes`.

  Unicode= %^UNICODE is boolean specification whether the Strings are in WIDE (UTF-16) encoding. By default (if omitted) it copies the global option `EUROASM Unicode=`.
Output
  CF=0
Error
  CF=1 if not all characters were written or if Handle was invalid.
Depends on
  WinABI
Tested by
  twinabi.htm
Examples
  StdOutput Message, Eol=Yes StdOutput Eol=Yes ; Write new line (CR+LF) only. StdOutput ="Error writing to file ",FileName, Handle=STD_ERROR_HANDLE

    StdOutput %MACRO  String1,String2,,,Size=-1, Handle=-11, Eol=No, Console=%StdConsole, Unicode=%^UNICODE
          IMPORT WriteFile,WriteConsoleA,WriteConsoleW
    C     %IF %Console
    U       %IF %Unicode
              %StdOutputFlags %SETA 2 + 1
            %ELSE U
              %StdOutputFlags %SETA 2 + 0
            %ENDIF U
          %ELSE C
    W       %IF %Unicode
              %StdOutputFlags %SETA 0 + 1
            %ELSE W
              %StdOutputFlags %SETA 0 + 0
            %ENDIF W
          %ENDIF C
    ArgNr %FOR 1..%#, STEP=1 ; Call the runtime for each String.
            PUSHQ %StdOutputFlags
    S       %IF TYPE#(SEGMENT# %1) = 'N' ; String is specified as a scalar, e.g. RSI.
              PUSHQ %1
            %ELSE S          ; String is specified as a symbolic address, e.g. Msg or ="Msg".
              PUSH RAX
              LEA RAX,[%1]
              XCHG RAX,[RSP]
            %ENDIF S
            PUSHQ %Size, %Handle
            CALL StdOutputWin64@RT
            %SHIFT 1         ; The next string to output.
          %ENDFOR ArgNr
    Eol   %IF %Eol
    U       %IF %Unicode
              PUSHQ %StdOutputFlags + 4, 0, 4, %Handle
            %ELSE U
              PUSHQ %StdOutputFlags + 4, 0, 2, %Handle
            %ENDIF U
            CALL StdOutputWin64@RT
          %ENDIF Eol
    StdOutputWin64@RT: PROC1 ; Invoked in stdcall convention with parameters Handle,Size,Addr,Flags.
            PUSHQ RAX,RCX,RDX,RDI,R8,R9,R10,R11,0,0
             %StdOutputWritten %SET RSP+0*8   ; The number of written characters.
             %StdOutputEol$    %SET RSP+1*8   ; Local string CR+LF.
             %StdOutputHandle  %SET RSP+11*8  ; %Handle
             %StdOutputSize    %SET RSP+12*8  ; %Size
             %StdOutputString  %SET RSP+13*8  ; %String
             %StdOutputFlags   %SET RSP+14*8  ; %flags specified by %Unicode, %Console, %Eol.
             MOV RCX,[%StdOutputHandle]
             WinABI GetStdHandle,RCX,Fastmode=Yes
             CMP RAX,-1
             STC
             JZ .90:           ; Abort with CF when INVALID_HANDLE_VALUE (-1).
             MOV R8,RAX        ; StdOutput handle.
             MOV RCX,[%StdOutputSize]
             MOV RDI,[%StdOutputString]
             MOV RDX,[%StdOutputFlags] ; Bits 0=Unicode, 1=Console, 2=Eol.
             MOV R11,WriteFile:
             TEST DL,2         ; Select File or Console mode.
             JZ .10:
             MOV R11,WriteConsoleA:
             TEST DL,1         ; Select ANSI or WIDE variant.
             JZ .10:
             MOV R11,WriteConsoleW:
         .10:TEST DL,4         ; Test if the string should be Eol.
             JZ .20:
             LEA RDI,[%StdOutputEol$] ; EOL is requested instead of string.
             MOVD [RDI],0x000A_000D ; WIDE EOL.
             TEST DL,1
             JNZ .20:
             MOVW [RDI],0x0A0D ; ANSI EOL.
         .20:XOR EAX,EAX       ; RDI,RCX is ASCIIZ string.
             MOV R10,RDI
             TEST DL,1         ; ASCII | WIDE.
             JZ .40:
             SHR ECX,1         ; Convert size to length in characters.
             REPNE SCASW       ; Find the WIDE zero terminator.
             JNE .50:
             SUB RDI,2         ; Omit the zero terminator.
             JMP .50:
         .40:REPNE SCASB       ; Find the ANSI zero terminator.
             JNE .50:
             DEC RDI
         .50:SUB RDI,R10       ; RDI is now the string size in bytes.
             AND DL,3
             XOR DL,3
             JNZ .60:
             SHR EDI,1         ; RDI is now string size in WIDE characters for Console.
             MOV RDX,RSP
         .60:MOV RCX,R8        ; File/Console handle.
             MOV R9,RSP        ; Characters-written variable.
             MOV R8,RDI        ; String length in characters.
             MOV RDX,R10       ; Pointer to string.
             WinABI R11,RCX,RDX,R8,R9,RAX,Fastmode=Yes
             CMP [%StdOutputWritten],RDI ; Set CF if not all characters were written.
        .90:POP RAX,RAX,R11,R10,R9,R8,RDI,RDX,RCX,RAX
            RET 4*8
          ENDP1 StdOutputWin64@RT:
     %ENDMACRO StdOutput

↑ StdInput Buffer, Size=, Handle=-10, Console=No, Unicode=%^UNICODE

  Macro StdInput reads a line of text terminated with CR from standard input device (usually the keyboard) specified by the Handle identifier.

Input
  Buffer is offset of memory where the input string will be stored. It can be a GPR, too.
  Size= is the Buffer size in bytes. If omitted (default), macro will use SIZE# attribute of the Buffer.
  Handle=-10 is the Windows standard input handle identifier. Possible input values are defined in winscon.htm:
  - STD_INPUT_HANDLE = -10
  Console=No (or Yes) is boolean specification whether the macro should use WinABI function ReadFile or ReadConsole.
  Unicode=%^UNICODE is boolean specification whether the characters read from console should be in WIDE (UTF-16) encoding. It is ignored when Console=No.

  > Input of ReadFile (default) is redirectable, but it does not distinguish WIDE or ANSI characters. ReadConsole mode can only be used with a console input buffer handle (no redirection by command-line operators `<` or `|` is working) but it interprets WIDE characters properly.

Output
  CF=0, RCX=number of bytes read.
Error
  CF=1, RCX=0.
Tested by
  twinabi.htm

    StdInput %MACRO Buffer, Size=, Handle=-10, Console=No, Unicode=%^UNICODE
        IMPORT ReadFile,ReadConsoleW,ReadConsoleA
    C   %IF %Console
    U     %IF %Unicode
            %StdInputFlags %SETA 2 + 1
          %ELSE U
            %StdInputFlags %SETA 2 + 0
          %ENDIF
        %ELSE C        ; ReadFile variant. Ignores Unicode.
    W     %IF %Unicode
            %StdInputFlags %SETA 0 + 1
          %ELSE W
            %StdInputFlags %SETA 0 + 0
          %ENDIF W
        %ENDIF C
        PUSHQ %StdInputFlags
    B   %IF TYPE#(SEGMENT# %Buffer) = 'N' ; Buffer is specified as a scalar, e.g. RSI.
           PUSHQ %Buffer
        %ELSE B          ; Buffer is specified as a symbolic address, e.g. Answer or [Answer+RDI].
    M     %IF '%Buffer[1]' === '['
            PUSH RAX
            LEA RAX,%Buffer
            MOV RAX,[RAX]
            XCHG RAX,[RSP]
          %ELSE M
            PUSH RAX
            LEA RAX,[%Buffer]
            XCHG RAX,[RSP]
          %ENDIF M
        %ENDIF B
    S   %IF '%Size' === ''
          PUSHQ SIZE# %Buffer
        %ELSE S
          PUSHQ %Size
        %ENDIF S
        PUSHQ %Handle
        CALL StdInputWin64@RT
    StdInputWin64@RT: PROC1
        PUSHQ RAX,RDX,R8,R9,R10,R11,0
         %ReturnedRCX    %SET RSP+0*8
         %StdInputHandle %SET RSP+8*8
         %StdInputSize   %SET RSP+9*8
         %StdInputBuffer %SET RSP+10*8
         %StdInputFlags  %SET RSP+11*8
         MOV RCX,[%StdInputHandle]
         WinABI GetStdHandle,RCX,Fastmode=Yes
         CMP RAX,-1
         STC
         JZ .90:                  ; Abort with CF when INVALID_HANDLE_VALUE (-1).
         MOV RCX,RAX
         MOV RDX,[%StdInputFlags] ; Bits 0=Unicode, 1=Console.
         MOV R8,[%StdInputSize]
         MOV R11,ReadFile
         TEST DL,2
         JZ .20:
     .10:MOV R11,ReadConsoleA
         TEST DL,1
         JZ .20:
         MOV R11,ReadConsoleW
         SHR R8,1
     .20:CMP DL,3
         MOV RDX,[%StdInputBuffer]
         MOV R9,RSP              ; %ReturnedECX.
         PUSHFQ                  ; Remember in ZF if Unicode&&Console.
          WinABI R11,RCX,RDX,R8,R9,0,Fastmode=Yes
         POPFQ
         CLC
         JNE .90:
         SALQ [RSP],1            ; Convert read character to bytes when Unicode&&Console.
    .90:POPQ RCX,R11,R10,R9,R8,RDX,RAX
        RET 4*8
       ENDP1 StdInputWin64@RT:
     %ENDMACRO StdInput

↑ TerminateProgram Errorlevel=0
  This macro provides exit from the running process and the return to the operating system.
  It also specifies the Errorlevel (plain number) which can be used to inform the batch script which launched the program whether the program terminated normally or due to some error condition.
Input
  Errorlevel= is the return code of the terminating program.
  Beside the keyword `Errorlevel=`, this value may also be specified as an ordinal operand.
  When this argument is omitted, it defaults to 0.
Output
  is not applicable.
Example
  TerminateProgram Errorlevel=[WorstErrLevel] ; Keyword value (from memory). TerminateProgram 8 ; Ordinal value.

    TerminateProgram %MACRO Errorlevel=0
         %IF %#=1 ; If ordinal provided.
           MOV RCX,%1
         %ELSE    ; If keyword provided.
           MOV RCX,%Errorlevel
         %ENDIF
         SUB RSP,4*8
         IMPORT ExitProcess, Lib="kernel32.dll"
         JMP ExitProcess
       %ENDMACRO TerminateProgram

       ENDHEAD winabi

▲Back to the top▲