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

# cpuext64.htm
Macros
Align2Qword
Clear
Compare
CopyTo
LodD
LodH
LodUTF8
StoD
StoH
StripApostrophes
StripColons
StripQuotes
StripSpaces

This file can be included to 64bit programs written in Euro Assembler.
The library contains OS-independent macroinstructions with calling convention similar to the standard Intel machine instruction set. They mostly pass operands in general-purpose registers and change the register contents on return.

Align* performs alignment at run time,
Clear, Compare, CopyTo provide some string operations,
Lod* converts numbers from human-readable notation to a binary number,
Sto* does the opposite,
Strip* trims the string from garbage characters which may surround it.

Similar macros with identical names for different program width are defined in cpuext16.htm and cpuext32.htm.

See also cpuext.htm for width-indepentent macros.

    cpuext64 HEAD

    ; Definition of flags used by macros in this library.
    %cpuextMinus    %SETA 0x80
    %cpuextSigned   %SETA 0x40
    %cpuextAlignL   %SETA 0x20
    %cpuextLeading0 %SETA 0x10
    %cpuextLowCase  %SETA 0x08

↑ Align2Qword
  This inline macro will align the 64bit GPR or contents of memory variable to QWORD value upward.
Input
  Variable is a 32bit register or memory operand.
Output
  CF=0, other registers unchanged.
Examples
  Align2Qword RCX Align2Qword [QwPointer]

    Align2Qword %MACRO Variable
                   ADDQ %Variable,  7
                   ANDQ %Variable, ~7
                %ENDMACRO Align2Qword

↑ Clear Operand, Size=, Filler=0
  This macro will fill the string in memory at Operand offset with the character Filler=.
Input
  Operand is pointer to a memory variable.
  Size= specifies number of bytes to fill. By default it is `SIZE# Operand`.
  Filler=0 is QWORD value whose LSB will be used to fill the Operand.
Output
  All registers are preserved.
Examples
  Clear Buffer1, Filler=" " ; Fill the whole memory variable Buffer1 with spaces. Clear RDI, Size=80, Filler=RAX ; Fill the memory at RDI with the contents of AL.

    Clear %MACRO Operand, Size=, Filler=0
            PUSHQ %Operand
            %IF "%Size" !=== ""
              PUSHQ %Size
            %ELSE
              PUSHQ SIZE#(%Operand)
            %ENDIF
            PUSHQ %Filler
            CALL Clear64@RT::
    Clear64@RT: PROC1
        PUSH RAX,RCX,RDX,RDI
          MOV RDX,[RSP+40] ; %Filler.
          MOV ECX,8
    .00:  SHRD RAX,RDX,8
          DEC ECX
          JNZ .00: ; Broadcast DL to all bytes or RAX.
          MOV RCX,[RSP+48] ; %Size.
          MOV RDI,[RSP+56] ; %Operand.
          SHR ECX,1
          JNC .10
          STOSB
    .10:  SHR ECX,1
          JNC .20
          STOSW
    .20:  SHR ECX,1
          JNC .30:
          STOSD
    .30:  JRCXZ .90
          REP:STOSQ
    .90:POP RDI,RDX,RCX,RAX
        RET 3*8
       ENDP1 Clear64@RT:
    %ENDMACRO Clear

↑ Compare Data1Ptr, Data1Size, Data2Ptr, Data2Size
  This macro will compare two data fields (strings) byte per byte.
Input
  Data1Ptr and Data1Size specify the first data field,
  Data2Ptr andData2Size the second one.
  The last parameter (Data2Size) may be omitted and it equals to Data1Size in this case.
Output
  ZF=1 if both fields are identical, otherwise ZF=0. All registers are preserved, .
Examples
  Compare F1, SIZE# F1, F2, SIZE# F2 Compare RDI,RSI,RCX

    Compare  %MACRO Data1Ptr,Data1Size,Data2Ptr,Data2Size
             %IF %# < 3                                                                                ; >
               %ERROR ID=5911, "Macro Compare requires 3 or 4 parameters."
               %EXITMACRO Compare
             %ENDIF
             %IF %# > 3
               PUSHQ %Data2Size,%Data2Ptr,%Data1Size,%Data1Ptr
             %ELSE
               PUSHQ %Data1Size,%Data2Ptr,%Data1Size,%Data1Ptr
             %ENDIF
             CALL Compare64@RT::
    Compare64@RT: PROC1
           PUSH RCX,RSI,RDI
            MOV RSI,[RSP+32] ; %Data1Ptr
            MOV RCX,[RSP+40] ; %Data1Size
            MOV RDI,[RSP+48] ; %Data2Ptr
            CMP RCX,[RSP+56] ; %Data2Size
            JNE .90:
            REPE:CMPSB
       .90:POP RDI,RSI,RCX
           RET 4*8
      ENDP1 Compare64@RT:
    %ENDMACRO Compare

↑ CopyTo Destination, Source, Size=
  This macro will copy the memory content from Source to Destination.
Input
  Destination is a pointer to target memory.
  Source is a pointer to the source of data.
  Size= specifies number of bytes to copy. By default it is the SIZE# Destination attribute, if possible. The size may be alternatively specified with the keyword Size=.
Output
  All registers are preserved.
Examples
  CopyTo Buffer1, Buffer2 CopyTo RDI,RSI,RCX CopyTo RDI,Line,Size=80

    CopyTo  %MACRO Destination, Source, Size=
            %IF "%Size" !=== ""
              PUSHQ %Size
            %ELSE
              %IF TYPE# %Destination = 'R'
                %ERROR ID=5912, "CopyTo data size not specified."
                %EXITMACRO CopyTo
              %ELSE
                PUSHQ SIZE# (%Destination)
              %ENDIF
             %ENDIF
          PUSHQ %Source, %Destination
          CALL CopyTo64@RT::
    CopyTo64@RT: PROC1
           PUSH RCX,RSI,RDI
            MOV RCX,[RSP+48] ; %Size
            MOV RSI,[RSP+40] ; %Source
            MOV RDI,[RSP+32] ; %Destination
            SHR ECX,1
            JNC .10:
            MOVSB
       .10: SHR ECX,1
            JNC .20:
            MOVSW
       .20: SHR ECX,1
            JNC .30:
            MOVSD
       .30: JRCXZ .90
            REP:MOVSD
       .90:POP RDI,RSI,RCX
           RET 3*8
      ENDPROC1 CopyTo64@RT:
    %ENDMACRO CopyTo

↑ LodD Source, Size=
  Load signed or unsigned integer number in decimal notation and return its binary value in RAX.
  LodD tolerates leading white spaces before the number and underscores `_` inside the notation. The number may begin with `+` or `-`, at least one digit must immediately follow the sign.
  Parsing stops when non-digit character is encountered or when the number of characters specified with Size= has been examinated.
Input
  Source is a pointer to ANSI-encoded decimal number.
  When the Source operand is not specified explicitly, RSI is assumed.
  Size= specifies length of the source field in characters (=bytes). By default it is unlimited.
Output
  CF=0, RAX=loaded number in the range -9_223_372_036_854_775_808 .. 18_446_744_073_709_551_615.
  RSI points behind the last parsed digit.
Error
  CF=1, RAX=0
  RSI= points at the unexpected character
  when the source number has wrong format:
  1. After skipping the leading white spaces Source does not start with a digit or sign.
  2. No digit immediately follows the sign.
  3. Loaded number does not fit into 64 bits.
Example
  InputField DB " -123.45" LodD InputField ; Loads RAX with -123d=0xFFFFFFFF_FFFFFF85
Tested by
  tmac64.htm

    LodD  %MACRO  Source, Size=
        %IF "%Size" === ""
          %IF "%Source" === "" || TYPE# %Source = 'R'
            PUSHQ -1
          %ELSE
            PUSHQ SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHQ %Size
        %ENDIF
        %IF "%Source" === ""
          PUSH RSI
        %ELSE
          PUSHQ %Source
        %ENDIF
        CALL LodD64@RT:
    LodD64@RT: PROC1
         PUSH RCX,RDX,RBX,RDI
           MOV RDX,[RSP+48] ; Size.
           MOV RSI,[RSP+40] ; Source.
           MOV RCX,RDX
           ADD RDX,RSI      ; Parse end limit.
           INC RCX
           JNZ .00:
           MOV RDX,-1
     .00:  SUB EAX,EAX
           SUB ECX,ECX      ; Signum.
           SUB EBX,EBX      ; Output value accumulator.
           MOV EDI,10
     .10:  CMP RSI,RDX
           JNB .Error:
           LODSB
           CMP AL,' '
           JBE .10:         ; Skip leading white spaces.
           ; Sign or digit expected.
           CMP AL,'+'
           JE .20:
           CMP AL,'-'
           JNE .30:
           DEC RCX          ; RCX is Minus flag.
     .20: ; At least one digit expected behind the signum, otherwise error.
           CMP RSI,RDX
           JNB .Error:
           LODSB
     .30:  SUB AL,'0'
           JB .Error:
           CMP AL,9
           JA .Error:
           MOV EBX,EAX      ; Most significant digit loaded.
     .40: ; Other digits or underscores are expected, otherwise the parsing stops.
           CMP RSI,RDX
           JNB .EndOfNumber:
           LODSB
           CMP AL,'_'
           JE .40:
           SUB AL,'0'
           JB .EndOfNumber1:
           CMP AL,9
           JA .EndOfNumber1:
           XCHG RAX,RBX
           PUSH RDX
            MUL RDI
           POP RDX
           JC .Error:
           ADD RBX,RAX
           JC .Error:
           SUB EAX,EAX
           JMP .40:
     .Error:DEC RSI
            XOR EAX,EAX
            STC
            JMP .95:
     .EndOfNumber1:DEC RSI
     .EndOfNumber:
           TEST ECX       ; Minus?
           JZ .90:
           NEG RBX
      .90: CLC
           MOV RAX,RBX
      .95:POP RDI,RBX,RDX,RCX
         RET 2*8
      ENDPROC1 LodD64@RT:
    %ENDMACRO LodD

↑ LodH Source, Size=, Align=left
  Load 64bit number in hexadecimal notation to register RAX. Number may contain hexadecimal digits '0'..'9', 'a'..'f', 'A'..'F' optionally prefixed with '+' or '-' sign. LodH tolerates underscore '_' among digits. At least one hexadecimal digit should immediately follow the sign. Parsing stops when other non-hexadigit character is encountered or when Size= is exhausted.
  Prefix `0x` is not supported (LodH would return RAX=0 and RSI behind the `0x`.
Input
  Source is a pointer to the ANSI-encoded hexadecimal number.
  RSI is assumed when the Source operand is not specified explicitly.
  Size= is number of characters in the source field. When the size is not explicitly specified, `SIZE# %Source` is assumed. Size is not limited if it cannot be determined from %Source.
  Align=left. By default the source must start with sign or hexadecimal digit. LodH tolerates leading white spaces only when Align=right.
Output
  CF=0, RAX=loaded number
  RSI= pointer behind the last parsed source digit.
Error
  CF=1, RAX,RSI undefined
  when the source number has wrong format:
  1. source does not start with a hexadecimal digit or sign,
  2. no hexadecimal digit immediately follows the sign,
  3. loaded number does not fit into 64 bits.
Example
  InputField DB "3FCh" LodH InputField ; loads RAX with 0x3FC=1020, RSI points to 'h'

    LodH  %MACRO  Source, Size=, Align=left
        %LodHflags %SETA  %cpuextAlignL & "%Align[1]" == "L"
        PUSHQ %LodHflags
        %IF "%Size" === ""
          %IF "%Source" === "" || TYPE# %Source = 'R'
            PUSHQ -1
          %ELSE
            PUSHQ SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHQ %Size
        %ENDIF
        %IF "%Source" === ""
          PUSH RSI
        %ELSE
          PUSHQ %Source
        %ENDIF
        CALL LodH64@RT::
    LodH64@RT: PROC1
         PUSH RCX,RDX,RBX
           MOV RDX,[RSP+48] ; %LodHflags.
           MOV RCX,[RSP+40] ; %LodHSize.
           MOV RSI,[RSP+32] ; %LodHSource.
           SUB EAX,EAX
           SUB EBX,EBX      ; RBX will accumulate the binary value.
           JRCXZ .20:
           TEST DL,%cpuextAlignL
           JNZ .30:
      .10: LODSB            ; White spaces are tolerated when Align=right.
           CMP AL,' '
           JA .40:
           DEC ECX
           JNZ  .10:
      .20: STC ; Error.
           JMP .95:
      .30: LODSB            ; Signum or digit expected.
      .40: CMP AL,'+'
           JE .70:
           CMP AL,'-'
           JNE .60:
           OR DL, %cpuextMinus
           JMP .70:
      .50: LODSB            ; Digit expected.
      .60: CMP AL,'_'
           JE .70:
           SUB AL,'0'
           JB .80:
           CMP AL,9
           JNA .65:
           SUB AL,'A'-'0'-10
           JB .80:
           CMP AL,15
           JNA .65
           SUB AL,'a'-'A'
           JB .80:
           CMP AL,15
           JA .80:
      .65: SAL RBX,4
           ADD RBX,RAX
           OR DL, %cpuextLeading0 ; At least one digit was found.
      .70: DEC ECX
           JNZ .50:
      .80: TEST DL, %cpuextLeading0
           JZ .20:                ; Error if no digit was parsed.
           TEST DL, %cpuextMinus
           JZ .90:
           NEG RBX
      .90: CLC
           MOV RAX,RBX
     .95:POP RBX,RDX,RCX
         RET 3*8
      ENDP1 LodH64@RT:
    %ENDMACRO LodH

↑ StoD Destination, Size=, Signed=yes, Align=left, LeadingZeroes=no
  Store 64bit integer from RAX in decimal notation.
Input
  RAX contains the input number.
  Destination is a pointer to memory where the decimal number will be stored. RDI is assumed when Destination is omitted.
  Size= specifies length of the Destination field. When the size is not explicitly specified, it is taken from `SIZE# %Destination` . Size is not limited if it cannot be determined from %Destination. Size will never exceed 20 characters. The output string is ANSI (1byte per digit).
  Signed=yes specifies whether the input number is treated as signed. Negative numbers will be prefixed with minus sign `-`, positive sign is not used.
  Align=left specifies alignment (justification) of the number in Destination field. If Size= cannot be determined, this parameter is ignored. When Align=right and Size= is specified, the vacant characters on the left side will be filled with spaces or unsignificant leading zeroes.
  LeadingZeroes=no specifies if the whole field should be filled with unsignificant `0` from the left side. Otherwise spaces would be used.
  When Align=left or when the Size= is not specified, leading zeroes|spaces are not used.
Output
  CF=0,
  RDI points behind the last stored digit.
Error
  CF=1 if Size= is not long enough,
  RDI points to Destination, nothing is stored.
Example
  OutputField DB 20 * B '?' MOV EAX,123 StoD OutputField MOV AL,'.' STOSB MOV EAX,45 StoD ; OutputField is now "123.45??????????????", ; RDI points to the 1st question mark
Layout
  MOV RAX, 12345 MOV RAX, -12345 StoD Size=8 StoD Size=8 ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │1│2│3│4│5│ │ │ │ │-│1│2│3│4│5│ │ │ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI MOV RAX, 12345 MOV RAX, -12345 StoD Size=8, Align=right StoD Size=8, Align=right ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │ │ │ │1│2│3│4│5│ │ │ │-│1│2│3│4│5│ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI MOV RAX, 12345 MOV RAX, -12345 StoD Size=8, Align=right,\ StoD Size=8, Align=right,\ LeadingZeroes=yes LeadingZeroes=yes ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │0│0│0│1│2│3│4│5│ │-│0│0│1│2│3│4│5│ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI
Tested by
  tmac64.htm

    StoD  %MACRO  Destination, Size=, Signed=Yes, Align=Left, LeadingZeroes=No
        %StoDflags %SETA              %cpuextSigned   & "%Signed[1]" == "Y"
        %StoDflags %SETA %StoDflags | %cpuextAlignL   & "%Align[1]" == "L"
        %StoDflags %SETA %StoDflags | %cpuextLeading0 & "%LeadingZeroes[1]" !== "N"
        %IF "%Align[1]" !== "L" && "%Size" === "" && ("%Destination" === "" || "%Destination" == "RDI")
          %ERROR ID=5913, "StoD cannot Align=Right when Size= is not specified."
          %StoDflags %SETA %StoDflags | %cpuextAlignL
        %ENDIF
        PUSHQ %StoDflags
        %IF "%Size" === ""
          %IF "%Destination" === "" || TYPE# %Destination = 'R'
            PUSHQ -1
          %ELSE
            PUSHQ SIZE# (%Destination)
          %ENDIF
        %ELSE
          PUSHQ %Size
        %ENDIF
        %IF "%Destination" === ""
          PUSH RDI
        %ELSE
          PUSHQ %Destination
        %ENDIF
        CALL StoD64@RT:
    StoD64@RT: PROC1
        PUSH RAX,RCX,RDX,RBX,RBP,RSI
          SUB RSP,20+4       ; Room for temporary destination.
          MOV RDI,RSP
          MOV RCX,[RSP+96]   ; %StoDflags.
          MOV RBX,RAX        ; The input integer number is now in RBX.
          TEST CL,%cpuextSigned
          JZ .10:
          TEST RBX
          JNS .10:
          NEG RBX            ; Convert RBX to positive number.
          OR CL,%cpuextMinus ; Result will be prefixed with '-'.
    .10:
    divsr %FOR 10000000000000000000, \ 1019.
                1000000000000000000, \ 1018.
                 100000000000000000, \ 1017.
                  10000000000000000, \ 1016.
                   1000000000000000, \ 1015.
                    100000000000000, \ 1014.
                     10000000000000, \ 1013.
                      1000000000000, \ 1012.
                       100000000000, \ 1011.
                        10000000000, \ 1010.
                         1000000000, \ 109.
                          100000000, \ 108.
                           10000000, \ 107.
                            1000000, \ 106.
                             100000, \ 105.
                              10000, \ 104.
                               1000, \ 103.
                                100, \ 102.
                                 10  ; 101.
            MOV RBP,%divsr
            CALL .Divide:
     .Divide:PROC1           ; Divide divident RBX by divisor RBP.
              SUB EDX,EDX
              XCHG RBX,RAX   ; Zero extend divident RBX to RDX:RAX.
              DIV RBP        ; RAX is now 0..9.
              ADD AL,'0'     ; Convert to decimal digit.
              STOSB          ; Store to temporary destination.
              XCHG RDX,RBX   ; Copy remainder to RBX.
              RET
             ENDP1 .Divide:
          %ENDFOR divsr      ; Repeat with smaller divisor.
          MOV AL,BL          ; The last remainder.
          ADD AL,'0'         ; Convert to decimal digit.
          STOSB              ; String of 20 digits at RSP now contains the temporary result.
          MOV RSI,RSP        ; Pointer to temporary result with leading decimal zeros.
          LEA RBX,[RSP+20-1] ; Pointer to the last digit in temporary result.
    .20:  CMP RSI,RBX
          JNB .30:           ; If no more zeros to skip.
          LODSB
          CMP AL,'0'
          JE .20:            ; Skip leading unsignificant zeros.
          DEC RSI
    .30:  INC RBX
          SUB RBX,RSI        ; Unsigned result without leading zeros is now in RSI,RBX.
          MOV RDX,[RSP+88]   ; %StoDSize.
          MOV RDI,[RSP+80]   ; %StoDDestination.
          TEST RDX
          STC
          JZ .Overflow:
          TEST CL,%cpuextMinus
          JZ .40:
          DEC RDX
    .40:  SUB RDX,RBX        ; RDX is now the number of padding bytes in destination.
          JC .Overflow:
          TEST CL,%cpuextAlignL
          JZ .50:
          TEST CL,%cpuextMinus
          JZ .90:
          MOV AL,'-'
          STOSB
          JMP .90:
    .50:  ; Justify the result to the right.
          TEST CL,%cpuextMinus
          JZ .70:
          TEST CL,%cpuextLeading0
          MOV ECX,EDX
          JZ .60:
          MOV AL,'-'
          STOSB
          MOV AL,'0'
          REP:STOSB
          JMP .90:
    .60:  MOV AL,' '
          REP STOSB
          MOV AL,'-'
          STOSB
          JMP .90:
    .70:  TEST CL,%cpuextLeading0
          MOV AL,' '
          JZ .80:
          MOV AL,'0'
    .80:  MOV ECX,EDX
          REP:STOSB
    .90:  MOV ECX,EBX
          REP:MOVSB
    .Overflow:  ; CF signalizes error.
          LEA RSP,[RSP+20+4]
        POP RSI,RBP,RBX,RDX,RCX,RAX
        RET 3*8
      ENDP1 StoD64@RT:
    %ENDMACRO StoD

↑ StoH Destination, Size=, Signed=no, Case=upper, Align=right, LeadingZeroes=yes
  Store 64bit integer number from RAX converted to hexadecimal notation (without radix modifier `0x` and `H`. The number is always treated as signed. When it is negative, its converted first significant digit has the MSbit=1 (`8..F`). First significant digit of positive number is `1..7`.
Input
  RAX contains the input binary number.
  Destination is a pointer to memory where the number in hexadecimal notation will be stored. RDI is assumed when Destination is omitted.
  Size= specifies the number of bytes in the Destination field. When the size is not explicitly specified, it is taken from `SIZE# %Destination`. Size is not limited if it cannot be determined from %Destination but it will never exceed 16 bytes. The output string is ANSI (1byte per digit).
  Signed=no if the number should be treated as signed.
  Case=upper (or lower). This parameter will select between `ABCDEF` and `abcdef` digits.
  Align=right (or left) specifies alignment (justification) of the number in Destination field. If Size= cannot be determined, this parameter is ignored and the result is left-justified. Only significant hexadecimal digits are stored (at least one).
  When Align=right and Size= is specified, the vacant room on the left side will be filled with spaces or unsignificant leading characters `0` or `F` (this depends on the signum of input value).
  LeadingZeroes=yes (or no) specifies if the whole field should be filled from the left side with unsignificant `0` or `F`. When Align=left or when the Size= is not determined, leading characters are not used and only significant nibbles are stored.
Output
  CF=0,
  RDI points behind the last stored destination digit.
Error
  CF=1 if Size= is not long enough to store all significant digits without overflow,
  RDI points to Destination, nothing is stored.
Layout
  MOV RAX, 0x123 MOV RAX, -0x123 StoH Size=8 StoH Size=8, Signed=yes ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │0│0│0│0│0│1│2│3│ │F│F│F│F│F│E│D│D│ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI MOV RAX, 0x123 MOV RAX, -0x123 StoH Size=8, \ StoH Size=8, Signed=yes, \ LeadingZeroes=no LeadingZeroes=no ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │ │ │ │ │ │1│2│3│ │ │ │ │ │ │E│D│D│ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI MOV RAX, 0x123 MOV RAX, -0x123 StoH Size=8, Align=left StoH Size=8, Signed=yes, Align=left ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │1│2│3│ │ │ │ │ │ │E│D│D│ │ │ │ │ │ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ ^ ^ RDI RDI
Tested by
  tmac64.htm

    StoH  %MACRO  Destination, Size=, Case=Upper, Align=Right, LeadingZeroes=Yes, Signed=No
        %StoHflags %SETA              %cpuextLowCase  & "%Case[1]"   !== "u"
        %StoHflags %SETA %StoHflags | %cpuextSigned   & "%Signed[1]" !== "n"
        %StoHflags %SETA %StoHflags | %cpuextAlignL   & "%Align[1]"  !== "r"
        %StoHflags %SETA %StoHflags | %cpuextLeading0 & "%LeadingZeroes[1]" == "y"
        %IF "%Align[1]" == "R" && "%Size" === "" && ("%Destination" === "" || "%Destination" == "RDI")
          %ERROR ID=5915, "StoH cannot Align=Right when Size= is not specified."
          %StoHflags %SETA %StoHflags | %cpuextAlignL
        %ENDIF
        PUSHQ %StoHflags
        %IF "%Size" === ""
          %IF "%Destination" === "" || TYPE# %Destination = 'R'
            PUSHQ -1
          %ELSE
            PUSHQ SIZE# (%Destination)
          %ENDIF
        %ELSE
          PUSHQ %Size
        %ENDIF
        %IF "%Destination" === ""
          PUSH RDI
        %ELSE
          PUSH %Destination
        %ENDIF
        CALL StoH64@RT:
    StoH64@RT: PROC1
         PUSH RAX,RCX,RDX,RBX,RBP,RSI
           SUB RSP,16               ; Room for temporary destination.
           MOV RDI,RSP
           MOV RDX,[RSP+11*8]       ; %StoHflags.
           MOV RBX,RAX              ; Input value.
           MOV ECX,16               ; Number of 4bit nibbles in RBX.
           TEST DL,%cpuextLowCase
           JZ .10:
           OR DH,'x'^'X'            ; DH is 0x00 or 0x20 for upper or lower case.
    .10:   ROL RBX,4                ; Start with the most significant nibble.
           MOV AL,0x0F
           AND AL,BL
           OR  AL,'0'
           CMP AL,'9'
           JBE .20:
           ADD AL,'A'-'0'-10        ; AL='A'..'F'.
           OR  AL,DH                ; Convert to lower case if DH=0x20.
    .20:   STOSB
           DEC RCX
           JNZ .10:
           TEST DL,%cpuextSigned
           JNZ .50:
           MOV RSI,RSP              ; 16 bytes of temporary room at RSI is filled with hexadecimal digits.
           MOV CL,16
    .25:   LODSB
           CMP AL,'0'
           JNE .30:
           LOOP .25:
           INC ECX
    .30:   DEC RSI                  ; RCX is the number of significant digits.
           MOV RBP,[RSP+10*8]       ; %StoHSize.
           TEST RBP
           JNS .35:
           MOV EBP,ECX
    .35:   CMP RBP,RCX
           JC .Overflow:
           MOV RDI,[RSP+9*8]        ; %Destination.
           TEST DL,%cpuextAlignL
           JNZ .85:
    .40:   ; Right align RCX significant digits from RSI to RDI..RDI+RBP.
           SUB EBP,ECX              ; RBP is number of unsignificant zeroes.
           JNA .85:
           MOV AL,'0'
           TEST DL,%cpuextLeading0
           JNZ .45:
           MOV AL,' '
    .45:   STOSB
           DEC EBP
           JNZ .45:
           JMP .85:
    .50:   TEST RBX                 ; 16 bytes of temporary room at RSP is filled with hexadecimal digits.
           JNS .55:
           OR DH,'F'                ; Prepare DH=unsignificant digit 'F' or 'f'.
           JMP .60:
    .55:   MOV DH,'0'               ; Prepare DH=unsignificant digit '0'.
           JNZ .60:
           INC BL                   ; If input number = 0, one significant digit '0' is stored.
           JMP .70:
    .60:   BSR RCX,RBX              ; Find the most significat bit for both positive and negative value.
           NOT RBX
           BSR RBX,RBX
           CMP BL,CL
           JB .65:
           XCHG BL,CL               ; Select the minimum.
    .65:   ADD BL,5
           SHR BL,2                 ; RBX is now the number of significant output nibbles (1..16).
    .70:   LEA RSI,[RSP+ 2*8]       ; End of temporary room.
           MOV RCX,[RSP+10*8]       ; %StoHSize.
           MOV RDI,[RSP+ 9*8]       ; %StoHDestination.
           SUB RSI,RBX              ; RSI now points to 1st significant digit.
           TEST DL,%cpuextAlignL
           JNZ .80:
           SUB RCX,RBX
           JC .Overflow:
           MOV AL,DH
           TEST DL,%cpuextLeading0
           JNZ .75:
           MOV AL,' '
    .75:   REP:STOSB                ; Store RCX spaces or unsignificant digits.
    .80:   MOV RCX,RBX
    .85:   REP:MOVSB                ; Store significant digits.
    .Overflow:                      ; CF signalizes error.
           LEA RSP,[RSP+2*8]
         POP RSI,RBP,RBX,RDX,RCX,RAX
         RET 3*8
      ENDP1 StoH64@RT:
     %ENDMACRO StoH

↑ StripColons PtrReg, SizeReg, Unicode=%^UNICODE
  Remove leading and trailing colon(s) `:` from input field.
Input
  PtrReg is a register which points to the input field.
  SizeReg is a register with size of the input field in bytes. Both arguments must be specified as general purpose 64bit registers.
  Unicode= is a logical parameter which tells whether the strings is ANSI or WIDE.
Output
  Both input registers are changed if the field begins and|or ends with colons. If the input field only contains one or more colons, the 2nd register will return 0.

    StripColons %MACRO PtrReg, SizeReg, Unicode=%^UNICODE
          %IF TYPE#%PtrReg <> 'R' || TYPE#%SizeReg <> 'R'
            %ERROR ID=5918, "Both arguments of StripColons must be GPR."
            %EXITMACRO StripColons
          %ENDIF
          %IF %Unicode ; WIDE variant.
     StripColons1%.:
            CMP %SizeReg,2
            JL StripColons9%.:
            CMPW [%PtrReg+%SizeReg-2],":"
            JNE StripColons2%.:
            SUB %SizeReg,2
            JMP StripColons1%.:
     StripColons2%.:
            CMPW [%PtrReg],":"
            JNE StripColons9%.:
            ADD %PtrReg,2
            CMP %SizeReg,2
            JL StripColons9%.:
            SUB %SizeReg,2
            JMP StripColons2%.:
     StripColons9%.:
          %ELSE ; ANSI variant.
            TEST %SizeReg
            JZ StripColons9%.:
     StripColons1%.:
            CMPB [%PtrReg+%SizeReg-1],':'
            JNE StripColons2%.:
            DEC %SizeReg
            JNG StripColons9%.:
            JMP StripColons1%.:
     StripColons2%.:
            CMPB [%PtrReg],':'
            JNE StripColons9%.:
            INC %PtrReg
            DEC %SizeReg
            JMP StripColons2%.:
          %ENDIF
     StripColons9%.:
     %ENDMACRO StripColons

↑ StripQuotes PtrReg, SizeReg, Unicode=%^UNICODE
  Remove leading and trailing double quote `"` from input field.
Input
  PtrReg is a register which points to the input field.
  SizeReg is the register with size of the input field in bytes. Both arguments must be specified as general purpose 64bit registers.
  Unicode= is a logical parameter which tells whether the strings is ANSI or WIDE.
Output
  Both input registers are changed if the field begins and/or ends with double quote. If the input field only contains two quotes, the 2nd register will return 0.

    StripQuotes %MACRO PtrReg, SizeReg, Unicode=%^UNICODE
          %IF TYPE#%PtrReg <> 'R' || TYPE#%SizeReg <> 'R'
            %ERROR ID=5919, "Both arguments of StripQuotes must be GPR."
            %EXITMACRO StripQuotes
          %ENDIF
          %IF %Unicode  ; WIDE variant.
            CMP %SizeReg,2*2
            JB StripQuotes9%.:
            CMPW [%PtrReg+%SizeReg-2],'"'
            JNE StripQuotes1%.:
            SUB %SizeReg,2
     StripQuotes1%.:
            CMPW [%PtrReg],'"'
            JNE StripQuotes9%.:
            ADD %PtrReg,2
            SUB %SizeReg,2
     StripQuotes9%.:
          %ELSE  ; ANSI variant.
            CMP %SizeReg,2
            JB StripQuotes9%.:
            CMPB [%PtrReg+%SizeReg-1],'"'
            JNE StripQuotes1%.:
            DEC %SizeReg
     StripQuotes1%.:
            CMPB [%PtrReg],'"'
            JNE StripQuotes9%.:
            INC %PtrReg
            DEC %SizeReg
    StripQuotes9%.:
         %ENDIF
     %ENDMACRO StripQuotes

↑ StripApostrophes PtrReg, SizeReg, Unicode=%^UNICODE
  Remove leading and trailing single quote `'` from input field.
Input
  PtrReg is a register which points to the input field.
  SizeReg is register with size of the input field in bytes. Both arguments must be specified as general purpose 64bit registers.
  Unicode= is a logical parameter which tells whether the strings is ANSI or WIDE.
Output
  Both input registers are changed if the field begins and/or ends with apostrophe. If the input field only contains two apostrophes, the 2nd register will return 0.

    StripApostrophes %MACRO PtrReg, SizeReg, Unicode=%^UNICODE
          %IF TYPE#%PtrReg <> 'R' || TYPE#%SizeReg <> 'R'
            %ERROR ID=5917, "Both arguments of StripApostrophes must be GPR."
            %EXITMACRO StripApostrophes
          %ENDIF
          %IF %Unicode  ; WIDE variant.
            CMP %SizeReg,2*2
            JB StripApostrophes9%.:
            CMPW [%PtrReg+%SizeReg-2],"'"
            JNE StripApostrophes1%.:
            SUB %SizeReg,2
     StripApostrophes1%.:
            CMPW [%PtrReg],"'"
            JNE StripApostrophes9%.:
            ADD %PtrReg,2
            SUB %SizeReg,2
     StripApostrophes9%.:
          %ELSE  ; ANSI variant.
            CMP %SizeReg,2
            JB StripApostrophes9%.:
            CMPB [%PtrReg+%SizeReg-1],"'"
            JNE StripApostrophes1%.:
            DEC %SizeReg
     StripApostrophes1%.:
            CMPB [%PtrReg],"'"
            JNE StripApostrophes9%.:
            INC %PtrReg
            DEC %SizeReg
     StripApostrophes9%.:
          %ENDIF
     %ENDMACRO StripApostrophes

↑ StripSpaces PtrReg, SizeReg, Unicode=%^UNICODE
  Remove white spaces (characters equal or below ASCII 32) which surround the input field.
Input
  PtrReg is a register which points to the input field.
  SizeReg is a register with size of the input field in bytes. Both arguments must be specified as general purpose 64bit registers.
  Unicode= is a logical parameter which tells whether the strings is ANSI or WIDE.
Output
  Both input registers are changed if the field begins and/or ends with white spaces. If the input field contains nothing but spaces, the 2nd register will return 0.
Example
  Field: DB " some text ",13,10,0 MOV ESI,Field MOV ECX,SIZE# Field ; 14 StripSpaces ESI,ECX ; ESI=Field+1 ECX=9
Tested by
  tmac64.htm

    StripSpaces %MACRO PtrReg, SizeReg, Unicode=%^UNICODE
          %IF TYPE#%PtrReg <> 'R' || TYPE#%SizeReg <> 'R'
            %ERROR ID=5920, "Both arguments of StripSpaces must be GPR."
            %EXITMACRO StripSpaces
          %ENDIF
          %IF %Unicode ; WIDE variant.
     StripSpaces1%.:
            CMP %SizeReg,2
            JL StripSpaces9%.:
            CMPW [%PtrReg+%SizeReg-2]," "
            JA StripSpaces2%.:
            SUB %SizeReg,2
            JMP StripSpaces1%.:
     StripSpaces2%.:
            CMPW [%PtrReg]," "
            JA StripSpaces9%.:
            ADD %PtrReg,2
            CMP %SizeReg,2
            JL StripSpaces9%.:
            SUB %SizeReg,2
            JMP StripSpaces2%.:
     StripSpaces9%.:
          %ELSE  ; ANSI variant.
            TEST %SizeReg
            JZ StripSpaces9%.:
     StripSpaces1%.:
            CMPB [%PtrReg+%SizeReg-1]," "
            JA StripSpaces2%.:
            DEC %SizeReg
            JNG StripSpaces9%.:
            JMP StripSpaces1%.:
     StripSpaces2%.:
            CMPB [%PtrReg]," "
            JA StripSpaces9%.:
            INC %PtrReg
            DEC %SizeReg
            JMP StripSpaces2%.:
     StripSpaces9%.:
          %ENDIF
     %ENDMACRO StripSpaces

↑ LodUTF8 Source, EndSource

  Load one character encoded in UTF-8 from the source.

  Memory where the Source pointes, plus 1..4 bytes, must be allocated for reading.
  Overlong encoding is not tested.

Input
  Source is a pointer to UTF-8 encoded character|string. When the Source operand is not specified explicitly, RSI is assumed.
  EndSource is a pointer behind the string|character. When the EndSource operand is not specified explicitly, RDX is assumed.
Output
  CF=0, RAX=loaded Unicode point of the character. Same as UTF-32LE encoding.
  RSI points behind the last parsed byte (incremented by 1..4 bytes). Other registers unchanged.
Error
  CF=1, RAX=0xFFFD (= malformed input byte) or 0 when RSI>=RDX.
  RSI points behind the last parsed byte.

    LodUTF8 %MACRO Source
        %IF "%Source" !== ""
         %IF "%Source" !== "RSI"
           MOV RSI, %Source
         %ENDIF
        %ENDIF
        %IF "%EndSource" !== ""
         %IF "%EndSource" !== "RDX"
           MOV RDX, %EndSource
         %ENDIF
        %ENDIF
        XOR EAX,EAX
        CMP RSI,RDX
        CMC
        JC End%.:
        LODSB
        CMP AL,0x80
        CMC
        JNC End%.:       ; 7-bit ASCII character.
        PUSH RBX,RCX
          MOV EBX,EAX
          NOT BL
          BSR ECX,EBX    ; Scan bits 7..0 of the inverted first byte of UTF-8 character. CL=0..7.
          MOV BL,AL      ; First byte of 2,3,4 bytes long UTF-8 character (not inverted).
          MOV CH,0x7F    ; Prepare mask for payload bits in the 1st UTF-8 byte.
          SUB CL,7       ; CL=7,5,4,3 change to CL=0,-2,-3,-4.
          JZ Out%.:      ; When RBX is a codepoint 0..0x7F (7bit ASCII character).
          NEG CL         ; CL=2,3,4 (number of bytes in UTF-8 character).
          SHR CH,CL      ; CH=0x1F,0x0F,0x07 is payload mask.
          AND BL,CH      ; EBX will accumulate payload bits of codepoint.
          CMP CL,2
          JB Bad%.:
          CMP CL,4
          JA  Bad%.:
          DEC CL         ; CL=1, 2 or 3 continuation bytes expected.
          MOVZX EAX,CL
          ADD RAX,RSI
          CMP RAX,RDX    ; Check if there's at least that many input bytes left.
          JB Con%.:
    Bad%.:MOV EBX,0xFFFD
          STC
          JMP Out%.:
    Con%.:LODSB          ; Continuation byte AL=10xxxxxxb expected.
          BTR EAX,7      ; Reset the marker bit 7.
          JNC Bad%.:
          BTR EAX,6      ; Bit 6 must be 0.
          JC  Bad%.:
          SHL EBX,6      ; Make room in RBX for the next 6 bits.
          OR  BL,AL      ; Accumulate them.
          DEC CL
          JNZ Con%.:
    Out%.:MOV EAX,EBX    ; EAX=EBX is now the decoded codepoint 0..0x0F_FFFF.
        POP RBX,RCX
    End%.:
      %ENDMACRO LodUTF8

     ENDHEAD cpuext64

▲Back to the top▲