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

# cpuext16.htm
Macros
Align2Word
Align2Dword
Align2Qword
Clear
Compare
CopyTo
LodD
LodDD
LodH
LodHD
StoD
StoDD
StoH
StoHD

This file can be included to 16bit programs written in Euro Assembler.
The library contains OS-independent macroinstructions for 16bit programs with CPU 8086 with calling convention similar to the standard Intel 8086 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, CopyTo provide some string operations,
Lod* converts numbers from human-readable notation to a binary number,
Sto* does the opposite.

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

See also cpuext.htm for width-indepentent macros.

    cpuext16 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
    ; Saved values in standard BP frame.
    %Par4     %SET BP+24
    %Par3     %SET BP+22
    %Par2     %SET BP+20
    %Par1     %SET BP+18
    %ReturnAX %SET BP+14
    %ReturnCX %SET BP+12
    %ReturnDX %SET BP+10
    %ReturnBX %SET BP+08
    %ReturnSP %SET BP+06
    %ReturnBP %SET BP+04
    %ReturnSI %SET BP+02
    %ReturnDI %SET BP+00

↑ Align2Word
  This inline macro will align the 16bit GPR or contents of memory variable to WORD value upward.
Input
  Variable is 16bit register or memory operand.
Output
  CF=0, other registers unchanged.
Examples
  Align2Word CX Align2Word [wPointer]

    Align2Word %MACRO Variable
                  ADDW %Variable,  1
                  ANDW %Variable, ~1
               %ENDMACRO Align2Word

↑ Align2Dword
  This inline macro will align the 16bit GPR or contents of memory variable to DWORD value upward.
Input
  Variable is 16bit register or memory operand.
Output
  CF=0, other registers unchanged.
Examples
  Align2Dword CX Align2Dword [wPointer]

    Align2Dword %MACRO Variable
                   ADDW %Variable,  3
                   ANDW %Variable, ~3
                %ENDMACRO Align2Dword

↑ Align2Qword
  This inline macro will align the 16bit GPR or memory variable to QWORD value upward.
Input
  Variable is 16bit register or memory operand.
Output
  CF=0, registers unchanged.
Example
  Align2Qword CX ; CX changed from 12 to 16. Align2Qword [mem16]

    Align2Qword %MACRO Variable
                   ADDW %Variable,  7
                   ANDW %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
  ES= segment of the cleared Operand.
  Operand is pointer to a memory variable.
  Size= specifies number of bytes to fill. By default it is `SIZE# Operand`.
  Filler=0 is a WORD value whose LSB will be used to fill the Operand.
Output
  All registers are preserved.
Examples
  PUSH PARA# Buffer1 POP ES Clear Buffer1, Filler=" " ; Fill the whole memory variable Buffer1 with spaces. Clear DI, Size=80, Filler=AX ; Fill the memory at ES:DI with the contents of AL.

    Clear %MACRO Operand, Size=, Filler=0
            PUSHW %Filler
            %IF "%Size" !=== ""
              PUSHW %Size
            %ELSE
              PUSHW SIZE# (%Operand)
            %ENDIF
            PUSHW %Operand
            CALL Clear16@RT::
    Clear16@RT:: PROC1
        PUSHAW
          MOV BP,SP
          MOV AX,[%Par3] ; Filler.
          MOV CX,[%Par2] ; Size.
          MOV DI,[%Par1] ; Operand.
          MOV AH,AL
          SHR CX,1
          JNC .20
          STOSB
    .20:  JCXZ .90
          REP:STOSW
    .90:POPAW
        RET 3*2
       ENDP1 Clear16@RT::
    %ENDMACRO Clear

↑ Compare Data1Ptr, Data1Size, Data2Ptr, Data2Size
  This macro will compare two data fields (strings) byte per byte.
Input
  DS is segment of the first data field,
  ES is segment of the second data field,
  Data1Ptr and Data1Size specify the first data field,
  Data2Ptr and Data2Size 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 EDI,ESI,ECX

    Compare  %MACRO Data1Ptr,Data1Size,Data2Ptr,Data2Size
             %IF %# < 3                                                                                ; >
               %ERROR ID=5911, "Macro Compare requires 3 or 4 parameters."
               %EXITMACRO
             %ENDIF
             %IF %# > 3
               PUSHW %Data2Size,%Data2Ptr,%Data1Size,%Data1Ptr
             %ELSE
               PUSHW %Data1Size,%Data2Ptr,%Data1Size,%Data1Ptr
             %ENDIF
             CALL Compare16@RT::
    Compare16@RT:: PROC1
           PUSHAW
            MOV BP,SP
            MOV SI,[%Par1] ; %Data1Ptr
            MOV CX,[%Par2] ; %Data1Size
            MOV DI,[%Par3] ; %Data2Ptr
            CMP CX,[%Par4] ; %Data2Size
            JNE .90:
            REPE:CMPSB
       .90:POPAW
           RET 4*2
      ENDP1 Compare16@RT::
    %ENDMACRO Compare

↑ CopyTo Destination, Source, Size=
  This macro will copy the memory contents from Source to Destination.
Input
  ES= segment of Destination.
  DS= segment of Source.
  Destination is a pointer to target memory.
  Source is a pointer to the source of data.
  Size= specifies the number of bytes to copy. By default it is 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 DI,SI,Size=CX CopyTo DI,Line,Size=80

    CopyTo  %MACRO Destination, Source, Size=
            %IF "%Size" !=== ""
              PUSHW %Size
            %ELSE
              %IF TYPE# %Destination = 'R'
                %ERROR ID=5912, "CopyTo Size= is not specified."
                %EXITMACRO CopyTo
              %ELSE
                PUSHW SIZE# (%Destination)
              %ENDIF
             %ENDIF
          PUSHW %Source, %Destination
          CALL CopyTo16@RT::
    CopyTo16@RT:: PROC1
           PUSHAW
            MOV BP,SP
            MOV CX,[%Par3] ; %Size
            MOV SI,[%Par2] ; %Source
            MOV DI,[%Par1] ; %Destination
            CLD
            SHR CX,1
            JNC .20:
            MOVSB
       .20: JCXZ .90
            REP:MOVSW
       .90:POPAW
           RET 3*2
      ENDPROC1 CopyTo16@RT::
    %ENDMACRO CopyTo

↑ LodD Source, Size=
  Load signed or unsigned 16bit integer number in decimal notation and return its binary value in AX.
  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
  DS= is segment of Source.
  Source points to a string with the loaded number in decimal notation.
  When the Source operand is not specified explicitly, DS:SI is assumed.
  Size= specifies length of the source field in bytes. By default it is unlimited.
Output
  CF=0, AX=loaded number in range -32_768 .. 65_535.
  DS:SI points behind the last parsed digit.
Error
  CF=1, AX,SI unchanged 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 16 bits.
Example
  InputField DB " -123.45" LodD InputField ; Loads AX with -123d alias 0xFF85.
Tested by
  tmac16.htm

    LodD  %MACRO  Source, Size=
        %IF "%Size" === ""
          %IF "%Source" === "" || "%Source" == "SI"
            PUSHW -1
          %ELSE
            PUSHW SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Source" == ""
          PUSH SI
        %ELSE
          PUSHW %Source
        %ENDIF
        CALL LodD16@RT::
    LodD16@RT:: PROC1
         PUSHAW
           MOV BP,SP
           MOV DX,[%Par2]   ; Size.
           MOV SI,[%Par1]   ; Source.
           MOV CX,DX
           CLD
           ADD DX,SI        ; DX points to the end of Source string.
           INC CX
           JNZ .00:
           MOV DX,-1
     .00:  SUB AX,AX
           SUB CX,CX        ; Nonzero CX signalizes negative value.
           SUB BX,BX        ; Output value accumulator.
           MOV DI,10        ; Numeric base.
     .10:  CMP SI,DX
           JNB .Error:
           LODSB
           CMP AL,' '
           JBE .10:         ; Skip leading white spaces.
           CMP AL,'+'       ; Signum or digit expected.
           JE .20:
           CMP AL,'-'
           JNE .30:
           DEC CX
     .20:  CMP SI,DX        ; At least one digit following the signum is expected.
           JNB .Error:
           LODSB
     .30:  SUB AL,'0'
           JB .Error:
           CMP AL,9
           JA .Error:
           MOV BX,AX        ; Most significant digit was loaded.
     .40:  CMP SI,DX ; Other digits or underscores are expexted, otherwise the parser stops.
           JNB .EndOfNumber:
           LODSB
           CMP AL,'_'
           JE .40:
           SUB AL,'0'
           JB .EndOfNumber1:
           CMP AL,9
           JA .EndOfNumber1:
           XCHG AX,BX
           PUSH DX
            MUL DI
           POP DX
           JC .Error:
           ADD BX,AX
           JC .Error:
           SUB AX,AX
           JMP .40:
     .Error:DEC SI
           STC
           JMP .95:
     .EndOfNumber1:DEC SI
     .EndOfNumber:
           JECXZ .90:
           NEG BX
      .90: CLC
           MOV [%ReturnAX],BX
      .95: MOV [%ReturnSI],SI
         POPAW
         RET 2*2
      ENDPROC1 LodD16@RT::
    %ENDMACRO LodD

↑ LodDD Source, Size=
  Load signed or unsigned 32bit integer number in decimal notation and return its binary value in DX:AX.
  LodDD 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
  DS= is segment of Source.
  Source points to a string with number in decimal notation.
  When the Source operand is not specified explicitly, DS:SI is assumed.
  Size= specifies length of the source field in bytes. By default it is unlimited (max.32K).
Output
  CF=0, DX:AX=loaded number in range -2_147_483_648 .. 4_294_967+295.
  DS:SI points behind the last parsed digit.
Error
  CF=1, DX:AX,SI undefined 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 32 bits.
Example
  InputField DB " -123.45" LodDD InputField ; Loads AX with -123d alias 0xFF85 and DX=-1 alias 0xFFFF.

    LodDD %MACRO  Source, Size=
        %IF "%Size" === ""
          %IF "%Source" === "" || "%Source" == "SI"
            PUSHW 32K
          %ELSE
            PUSHQ SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Source" == ""
          PUSH SI
        %ELSE
          PUSHW %Source
        %ENDIF
        CALL LodDD16@RT::
    LodDD16@RT:: PROC1
         PUSHAW
           MOV BP,SP
           MOV DX,[%Par2]    ; Size.
           MOV SI,[%Par1]    ; Source.
           MOV CX,DX
           ADD DX,SI         ; Parse-end limit.
           JNC .10:
           MOV DX,-1
     .10:  INC CX
           JNZ .20:
           MOV DX,-1
     .20:  SUB AX,AX
           SUB CX,CX         ; Signum is positive if CX=0.
           SUB BX,BX         ; Output value accumulator.
           MOV [%ReturnAX],BX
           MOV [%ReturnDX],BX
           MOV DI,10 ; Numeric base.
     .30:  CMP SI,DX
           JNB .Error:
           LODSB
           CMP AL,' '
           JBE .30:          ; Skip leading white spaces.
           CMP AL,'+'        ; Signum or digit expected.
           JE .40:
           CMP AL,'-'
           JNE .50:
           DEC CX            ; Nonzero CX signalizes that the number is negative.
     .40:  CMP SI,DX         ; At least one digit following the signum is expected.
           JNB .Error:
           LODSB
     .50:  SUB AL,'0'
           JB .Error:
           CMP AL,9
           JA .Error:
           MOV [%ReturnAX],AX ; Most significant digit loaded.
     .60:  CMP SI,DX ; Other digits or underscores expected, otherwise the parser stops.
           JNB .EndOfNumber:
           XOR AX,AX
           LODSB
           CMP AL,'_'
           JE .60:
           SUB AL,'0'
           JB .EndOfNumber1:
           CMP AL,9
           JA .EndOfNumber1:
           MOV BX,AX
           MOV AX,[%ReturnDX]
           PUSH DX
             MUL DI
           POP DX
           JC .Error:
           MOV [%ReturnDX],AX
           MOV AX,[%ReturnAX]
           PUSH DX
             MUL DI
             ADD AX,BX
             MOV [%ReturnAX],AX
             ADC [%ReturnDX],DX
           POP DX
           JC .Error:
           JMP .60:
     .Error:DEC SI
           STC
           JMP .90:
     .EndOfNumber1:DEC SI
     .EndOfNumber:
           CLC
           JCXZ .90: ; If the number is positive.
           NOTW [%ReturnAX] ; Otherwise CX=1.
           NOTW [%ReturnDX]
           ADDW [%ReturnAX],CX
           ADCW [%ReturnDX],0
      .90: MOVW [%ReturnSI],SI
         POPAW
         RET 2*2
      ENDPROC1 LodDD16@RT::
    %ENDMACRO LodDD

↑ LodH Source, Size=, Align=left
  Load 16bit number in hexadecimal notation to register AX. Number may contain hexadecimal digits '0'..'9', 'a'..'f', 'A'..'F' optionally prefixed with '+' or '-' sign. At least one hexadecimal digit should immediately follow the sign. Parsing stops when non-hexadigit character is encountered or when Size= is exhausted.
  Prefix `0x` is not supported (LodH would return AX=0 and SI behind the `0x`.
Input
  DS= segment of Source.
  Source is a pointer to a string with the hexadecimal number.
  DS:SI 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 (max.32KB) if it cannot be determined from %Source.
  Align=left. By default the source must start with sign or hexadecimal digit. LodH will tolerate leading white spaces only when Align=Right.
Output
  CF=0, AX=loaded number
  DS:SI=behind the last parsed source digit.
Error
  CF=1, AX,SI 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 16 bits.
Example
  InputField DB "3FCh" LodH InputField ; loads AX with 0x03FC=1020, SI points to 'h'.

    LodH %MACRO  Source, Size=, Align=Left
        %LodHflags %SETA  %cpuextAlignL & "%Align[1]" == "L"
        PUSHW %LodHflags
        %IF "%Size" === ""
          %IF "%Source" === "" || "%Source" == "SI"
            PUSHW 32K
          %ELSE
            PUSHW SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Source" === ""
          PUSH SI
        %ELSE
          PUSHW %Source
        %ENDIF
        CALL LodH16@RT::
    LodH16@RT:: PROC1 ; %1=Source, %2=Size, %3=flags.
         PUSHAW
           MOV BP,SP
           MOV SI,[%Par1] ; Source.
           MOV CX,[%Par2] ; Size.
           SUB AX,AX
           SUB BX,BX      ; Accumulator of the result value.
           CLD
           JCXZ .20:
           TESTW [%Par3],%cpuextAlignL
           JNZ .30:
      .10: LODSB          ; White spaces are tolerated.
           CMP AL,' '
           JA .40:
           LOOP .10:
      .20: STC ; Error.
           JMP .95:
      .30: LODSB          ; Signum or digit expected.
      .40: CMP AL,'+'
           JE .70:
           CMP AL,'-'
           JNE .60:
           ORW [%Par3],%cpuextMinus
           JMP .70:
      .50: LODSB          ; Digit expected.
      .60: SUB AL,'0'
           JB .80:
           CMP AL,9
           JNA .65:
           SUB AL,'A'-'0'
           JB .80:
           CMP AL,5
           JNA .64:
           SUB AL,'a'-'A'
           JB .80:
           CMP AL,5
           JA .80:
      .64: ADD AL,10
      .65: SAL BX,4
           ADD BX,AX
           ORW [%Par3],%cpuextLeading0 ; At least one digit was found.
      .70: LOOP .50:
           JMP .85:
      .80: DEC SI
      .85: TESTW [%Par3],%cpuextLeading0
           JZ .20:
           TESTW [%Par3],%cpuextMinus
           JZ .90:
           NEG BX
      .90: CLC
           MOV [%ReturnAX],BX
           MOV [%ReturnSI],SI
     .95:POPAW
         RET 3*2
      ENDP1 LodH16@RT::
    %ENDMACRO LodH

↑ LodHD Source, Size=, Align=Left
  Load 32bit number in hexadecimal notation to register pair DX:AX. Source number may contain hexadecimal digits '0'..'9', 'a'..'f', 'A'..'F' optionally prefixed with '+' or '-' sign. At least one hexadecimal digit should immediately follow the sign. Parsing stops when non-hexadigit character is encountered or when Size= is exhausted.
  Prefix `0x` is not supported (LodHD would return DX=AX=0 and SI behind the `0x`.
Input
  DS= segment of Source.
  Source is offset of a string with the hexadecimal number.
  DS:SI 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 (max. 32KB) if it cannot be determined from %Source.
  Align=Left. By default the source must start with sign or hexadecimal digit. LodHD will tolerate leading white spaces only when Align=Right.
Output
  CF=0, DX:AX=loaded number
  DS:SI=behind the last parsed source digit.
Error
  CF=1, registers unchanged 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 32 bits.
Example
  InputField DB "1234FCh" LodHD InputField ; loads DX with 0x0012, AX with 0x34FC, DS:SI points to 'h'.

    LodHD  %MACRO  Source, Size=, Align=Left
        %LodHDflags %SETA  %cpuextAlignL & "%Align[1]" == "L"
        PUSHW %LodHDflags
        %IF "%Size" === ""
          %IF "%Source" === "" || "%Source" == "SI"
            PUSHW 32K
          %ELSE
            PUSHW SIZE# (%Source)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Source" === ""
          PUSH SI
        %ELSE
          PUSHW %Source
        %ENDIF
        CALL LodHD16@RT::
    LodHD16@RT:: PROC1 ; %1=Source, %2=Size, %3=flags.
         PUSHAW
           MOV BP,SP
           MOV SI,[%Par1] ; Source.
           MOV CX,[%Par2] ; Size.
           SUB AX,AX
           SUB DX,DX
           SUB BX,BX ; DX:BX will accumulate the result value.
           CLD
           JCXZ .20:
           TESTW [%Par3],%cpuextAlignL
           JNZ .30:
      .10: LODSB ; White spaces are tolerated.
           CMP AL,' '
           JA .40:
           LOOP .10:
      .20: STC ; Error.
           JMP .95:
      .30: LODSB ; Signum or digit expected.
      .40: CMP AL,'+'
           JE .70:
           CMP AL,'-'
           JNE .55:
           ORW [%Par3],%cpuextMinus
           JMP .70:
      .50: LODSB ; Hexadecimal digit or underscore expected.
      .55: CMP AL,'_'
           JNE .60:
           LOOP .50:
           JMP .85:
      .60: SUB AL,'0'
           JB .80:
           CMP AL,9
           JNA .65:
           SUB AL,'A'-'0'
           JB .80:
           CMP AL,5
           JNA .64:
           SUB AL,'a'-'A'
           JB .80:
           CMP AL,5
           JA .80:
      .64: ADD AL,10
      .65: ; AX is now a valid nibble 0x0000..0x000F. Make room for it in DX:BX.
       bit %FOR 15..12 ; SHLD would be better but this is 8086 library.
             SHL BX
             RCL DX
             JC .20: ; Abort on overflow.
           %ENDFOR bit
           ADD BX,AX
           ORW [%Par3],%cpuextLeading0 ; At least one digit was found.
      .70: LOOP .50:
           JMP .85:
      .80: DEC SI
      .85: TESTW [%Par3],%cpuextLeading0
           JZ .20: ; Abort if no digit parsed.
           TESTW [%Par3],%cpuextMinus
           JZ .90:
           NOT BX   ; Negate the result DX:BX.
           NOT DX
           ADD BX,1
           ADC DX,0
      .90: CLC
           MOV [%ReturnAX],BX
           MOV [%ReturnDX],DX
           MOV [%ReturnSI],SI
     .95:POPAW
         RET 3*2
      ENDP1 LodHD16@RT::
    %ENDMACRO LodHD

↑ StoD Destination, Size=, Signed=yes, Align=Left, LeadingZeroes=No
  Store 16bit integer from AX in decimal notation.
Input
  AX contains the input number.
  ES= segment of Destination.
  Destination is a pointer to memory where the decimal notation of the number will be stored. ES:DI 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 6 characters.
  Signed=Yes specifies whether the input number is treated as signed. Negative numbers will be prefixed with '-' sign, positive sign is not used.
  Align=Left specifies alignment 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. When Align=Left or when the Size= is not determined, leading zeroes are not used.
Output
  CF=0, Destination filled, DI points behind the last stored destination digit.
Error
  CF=1 on overflow (destination field was not long enough), DI is unchanged.
Example
  OutputField DB 12*B'?' MOV AX,123 StoD OutputField MOV AL,'.' STOSB MOV AX,45 StoD ; OutputField is now "123.45??????", ; ES:DI points to the 1st question mark.
Tested by
  tmac16.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" == "DI")
          %ERROR ID=5913, "StoD cannot Align=Right when Size= is not specified."
          %StoDflags %SETA %StoDflags | %cpuextAlignL
        %ENDIF
        PUSHW %StoDflags
        %IF "%Size" === ""
          %IF "%Destination" === "" || "%Destination" == "DI"
            PUSHW -1
          %ELSE
            PUSHW SIZE# (%Destination)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Destination" === ""
          PUSH DI
        %ELSE
          PUSHW %Destination
        %ENDIF
        CALL StoD16@RT::
    StoD16@RT:: PROC1
         PUSHAW
           MOV BP,SP
           PUSH ES
           SUB SP,2+6 ; Word with string size + 6 bytes of temporary string.
           CLD
           MOV DX,AX ; Input value.
           MOV DI,SP ; Temporary destination field, 6 bytes.
           PUSH SS
           POP  ES
           PUSHW 10,100,1000,10000
           MOV SI,SP
           MOV CX,4  ; Number of pushed constants.
           TESTW [%Par3],%cpuextSigned
           JZ .10:
           TEST DX
           JNS .10:
           MOV AL,'-'
           NEG DX
           ORW [%Par3],%cpuextMinus
           STOSB
      .10: LODSW [SS:SI] ; 10^4, 10^3, 10^2, 10^1.
           CMP DX,AX
           JNB .20
           LOOP .10:
           JMP .40:
      .20: XCHG BX,AX
           SUB AX,AX
           XCHG AX,DX
           DIV BX
           ADD AL,'0'
           STOSB
           LODSW [SS:SI]
           LOOP .20:
           SUB SI,2
      .40: MOV AX,DX
           ADD AL,'0'
           STOSB          ; The last digit.
           SUB DI,SI      ; Number of significant digits.
           MOV [BP-4],DI  ; Store the string size.
           MOV CX,[%Par2] ; Allocated string Size.
           SUB CX,DI      ; Number of padding characters when Align=right.
           MOV DI,[%Par1] ; Destination.
           MOV ES,[BP-2]  ; Pushed Destination segment.
           JB .95: ; If overflowed.
           TESTW [%Par3],%cpuextAlignL
           JNZ .80:
           CMPW [%Par2],-1
           JE .80:        ; If unspecified Size=.
           JCXZ .80:
           MOV AL,' '
           TESTW [%Par3],%cpuextLeading0
           JZ .70:
           TESTW [%Par3],%cpuextMinus
           JZ .60:
           DECW [BP-4]
           MOVSB [ES:DI],[SS:SI] ; Copy Minus sign.
     .60:  MOV AL,'0'
     .70:  REP STOSB ; Leading spaces or zeroes.
     .80:  MOV CX,[BP-4] ; Size of string with significant digits.
           REP MOVSB [ES:DI],[SS:SI] ; Significant digits.
           CLC
           MOV [%ReturnDI],DI
     .95:  MOV SP,BP
         POPAW
         RET 3*2
      ENDP1 StoD16@RT::
    %ENDMACRO StoD

↑ StoDD Destination, Size=, Signed=Yes, Align=Left, LeadingZeroes=No
  Store 32bit integer from DX:AX in decimal notation.
Input
  DX:AX contains the input number.
  ES= segment of Destination.
  Destination is offset to memory where the decimal notation of the number will be stored. ES:DI 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 11 characters.
  Signed=yes specifies whether the input number is treated as signed. Negative numbers will be prefixed with `-` sign, positive sign is not used.
  Align=left specifies alignment 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. When Align=left or when the Size= is not determined, leading zeroes are not used.
Output
  CF=0, Destination filled, DI points behind the last stored destination digit.
Error
  CF=1 on overflow (destination field was not long enough), DI is unchanged.
Example
  OutputField DB 12*B'?' MOV AX,0xFFFF MOV DX,1 StoDD OutputField StoDD ; OutputField is now "131071??????", ; ES:DI points to the 1st question mark.

    StoDD  %MACRO  Destination, Size=, Signed=Yes, Align=Left, LeadingZeroes=No
        %StoDDflags %SETA               %cpuextSigned   & "%Signed[1]" == "Y"
        %StoDDflags %SETA %StoDDflags | %cpuextAlignL   & "%Align[1]" == "L"
        %StoDDflags %SETA %StoDDflags | %cpuextLeading0 & "%LeadingZeroes[1]" !== "N"
        %IF "%Align[1]"!=="L" && "%Size" === "" && ("%Destination" === "" || "%Destination" == "DI")
          %ERROR ID=5913, "StoDD cannot Align=Right when Size= is not specified."
          %StoDDflags %SETA %StoDDflags | %cpuextAlignL
        %ENDIF
        PUSHW %StoDDflags
        %IF "%Size" === ""
          %IF "%Destination" === "" || "%Destination" == "DI"
            PUSHW -1
          %ELSE
            PUSHW SIZE# (%Destination)
          %ENDIF
        %ELSE
          PUSHW %Size
        %ENDIF
        %IF "%Destination" === ""
          PUSH DI
        %ELSE
          PUSHW %Destination
        %ENDIF
        CALL StoDD16@RT::
    StoDD16@RT:: PROC1 ; %1=Destination, %2=Size, %3=flags.
      PUSHAW
        MOV BP,SP
    %StoDD_Lo  %SET BP-2  ; Binary absolute value, low word.
    %StoDD_Hi  %SET BP-4  ; Binary absolute value, high word.
    %StoDDstr  %SET BP-16 ; 12 bytes of temporary room for decimal string.
        SUB SP,16         ; Allocate local variables.
        JNSt [%Par3],%cpuextSigned,.10:
        TEST DX
        JNS .10:
        SetSt [%Par3],%cpuextMinus ; Result will be prefixed with -.
        NOT AX
        NOT DX
        ADD AX,1
        ADC DX,0
    .10:MOV [%StoDD_Lo],AX
        MOV [%StoDD_Hi],DX
        PUSH ES,SS
          POP ES
          STD  ; Prepare to store decimal digits to %StoDDstr backward.
          LEA DI,[%StoDDstr+12-1]
          MOV CX,10 ; Divisor.
    .20:  MOV AX,[%StoDD_Hi]
          SUB DX,DX
          DIV CX
          MOV [%StoDD_Hi],AX
          MOV AX,[%StoDD_Lo]
          DIV CX
          MOV [%StoDD_Lo],AX
          XCHG AX,DX ; Put the remainder to AL.
          ADD AL,'0'
          STOSB
          TEST DX
          JNZ .20:   ; Repeat until quotient is zero.
        POP ES
        CLD
        LEA CX,[%StoDDstr+12-1]
        SUB CX,DI      ; CX is now the number of significant digits in string.
        LEA SI,[DI+1]  ; SI is offset of the 1st decimal digit.
        MOV DI,[%Par1] ; ES:DI is Destination.
        JSt [%Par3],%cpuextAlignL, .40:
        MOV DX,[%Par2] ; Size of Destination.
        SUB DX,CX
        JC .90:        ; Abort if overflow.
        JNSt [%Par3],%cpuextMinus, .30:
        DEC DX
        STC
        JS .90:        ; Abort if overflow.
    .30:JSt [%Par3],%cpuextLeading0,.50:
        MOV AL,' '
        XCHG CX,DX
        REP STOSB      ; Pad with spaces.
        XCHG DX,CX
    .40:JNSt [%Par3],%cpuextMinus, .80:
        MOV AL,'-'
        STOSB
        JMP .80:
    .50:JNSt [%Par3],%cpuextMinus, .60:
        MOV AL,'-'
        STOSB
    .60:MOV AL,'0'
        XCHG CX,DX
        REP STOSB       ; Pad with unsignificant zeroes.
        XCHG DX,CX
    .80:REP SEGSS MOVSB ; Copy significant digits to Destination.
        MOV [%ReturnDI],DI
        CLC
    .90:MOV SP,BP
      POPAW
      RET 3*2
     ENDP1 StoDD16@RT::
    %ENDMACRO StoDD

↑ StoH Destination, Size=, Align=Right, Case=Upper, LeadingZeroes=Yes
  Store 16bit signed integer number from AX in hexadecimal notation.
Input
  AX contains the input number.
  ES= is segment of Destination.
  Destination is offset of memory where the hexadecimal number will be stored. ES:DI is assumed when Destination is omitted.
  Size= specifies the length of the Destination field in bytes. 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 not exceed 4 characters in this case.
  Align=right (or left) specifies justification of the number in Destination field. If Size= cannot be determined, this parameter is ignored, the result is justified to the left and only significant digits are emitted. When Align=Right and Size= is specified, the vacant characters on the left side will be filled either with spaces (when LeadingZeroes=No), or with unsignificant leading digits `0` or `F`. Unsignificant `F`s are emitted only when the number is negative (MSbit of AX is set), and Align=Right and LeadingZeroes=Yes.
  Case=Upper (or Lower) will select between `ABCDEF` and `abcdef` digits.
  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 specified, leading characters are not used.
Output
  CF=0,
  ES:DI points behind the last stored destination digit.
Error
  CF=1, registers are unchanged on overflow (destination field was not long enough).
Examples
  OutputField DB 12*B'?' MOV AX,123 StoH OutputField, Size=3, Align=left ; OutputField is now '7B??????????', DI points to the 1st '?' StoH OutputField, Size=4 ; OutputField is now ' 007B???????', DI points to the 1st '?' MOV AX,-123 StoH OutputField, Size=4, Align=Left ; OutputField is now 'FF85?????????' StoH OutputField, Size=2, Align=Left ; OutputField is now '85?????????' StoH OutputField, Size=5, Align=Right ; OutputField is now 'FFF85???????'
Tested by
  tmac16.htm

    StoH  %MACRO  Destination, Size=, Align=Right, Case=Upper, LeadingZeroes=Yes
      %StoHflags %SETA              %cpuextLowCase  & "%Case[1]" !== "U"
      %StoHflags %SETA %StoHflags | %cpuextAlignL   & "%Align[1]" !== "R"
      %StoHflags %SETA %StoHflags | %cpuextLeading0 & "%LeadingZeroes[1]" == "Y"
      %IF "%Align[1]" == "R" && "%Size" === "" && ("%Destination" === "" || "%Destination" == "DI")
        %ERROR ID=5915, "StoH cannot Align=Right when Size= is not specified."
        %StoHflags %SETA %StoHflags | %cpuextAlignL
      %ENDIF
      PUSHW %StoHflags
      %IF "%Size" === ""
        %IF "%Destination" === "" || "%Destination" == "DI"
          PUSHW -1
        %ELSE
          PUSHW SIZE# (%Destination)
        %ENDIF
      %ELSE
        PUSHW %Size
      %ENDIF
      %IF "%Destination" === ""
        PUSH DI
      %ELSE
        PUSHW %Destination
      %ENDIF
      CALL StoH16@RT::
    StoH16@RT:: PROC1 ; %1=Destination, %2=Size, %3=flags.
      PUSHAW
        MOV BP,SP
    %StoHstr %SET BP-4
        SUB SP,4  ; Room for a temporary hexadecimal string.
        MOV DI,SP
        MOV DX,AX ; Copy input value.
        MOV BX,0x2000
        TESTW [%Par3],%cpuextLeading0
        JZ .35:
        MOV BH,'0'
    .10:TEST DX
        JNS .20:
        MOV BH,'F'
    .20:TESTW [%Par3],%cpuextLowCase
        JZ .35:
        OR BL,'x'^'X'  ; Convert 'A'..'Z'  to 'a'..'z' when BL=0x20.
    .30:OR BH,BL       ; BH is now unsignificant digit '0' or 'F' or 'f'.
    .35:MOV CX,0x0404  ; CL is nibble width, CH is nibble count.
    .40:ROL DX,CL
        MOV AX,0x000F
        AND AX,DX
        ADD AL,0x90    ; Convert the nibble in AL to a hexadigit ('0'..'F').
        DAA
        ADC AL,0x40
        DAA
        OR AL,BL       ; Adjust character case.
        MOV [SS:DI],AL
        INC DI
        DEC CH
        JNZ .40:
        ; Value from DX is now stored as 4 hexa digits in %StoHstr buffer.
        ; CX=4. Calculate the number of significant digits (1..4) into CX.
        TEST DX
        JNS .50:
        NOT DX
    .50:TEST DX,0xF800
        JNZ .60:
        DEC CX
        TEST DX,0xFF80
        JNZ .60:
        DEC CX
        TEST DX,0xFFF8
        JNZ .60:
        DEC CX
    .60:MOV AX,[%Par2] ; Size of destination buffer.
        SUB AX,CX
        JB .90:        ; Destination is not long enough.
        MOV DI,[%Par1] ; Offset of Destination in segment ES.
        MOV SI,SP      ; Offset of %StoHstr.
        ADD SI,4
        SUB SI,CX
        TESTW [%Par3],%cpuextAlignL
        JZ .80:
    .70:REP:SEGSS:MOVSB
        JMP .85:
    .80:; Right-aligned destination - store AX unsignificant characters first.
        MOV DX,CX
        XCHG AX,CX
        MOV AL,BH
        REP STOSB       ; Unsignificant digits.
        XCHG CX,DX
        REP:SEGSS:MOVSB ; Significant digits.
    .85:MOV [%ReturnDI],DI
    .90:MOV SP,BP
       POPAW
       RET 3*2
      ENDP1 StoH16@RT::
     %ENDMACRO StoH

↑ StoHD Destination, Size=, Align=Right, Case=Upper, LeadingZeroes=Yes
  Store 32bit signed integer number from DX:AX in hexadecimal notation.
Input
  DX:AX contains the input 32bit number.
  ES= is segment of Destination.
  Destination is offset of memory where the hexadecimal number will be stored. ES:DI is assumed when Destination is omitted.
  Size= specifies the length of the Destination field in bytes. 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 not exceed 8 characters in this case.
  Align=right (or left) specifies justification of the number in Destination field. If Size= cannot be determined, this parameter is ignored, the result is justified to the left and only significant digits are emitted. When Align=Right and Size= is specified, the vacant characters on the left side will be filled either with spaces (when LeadingZeroes=No), or with unsignificant leading digits `0` or `F`. Unsignificant `F`s are emitted only when the number is negative (MSbit of AX is set), and Align=Right and LeadingZeroes=Yes.
  Case=Upper (or Lower) will select between `ABCDEF` and `abcdef` digits.
  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 specified, leading characters are not used.
Output
  CF=0,
  ES:DI points behind the last stored destination digit.
Error
  CF=1, registers are unchanged on overflow (destination field was not long enough).
Examples
  OutputField DB 12*B'?' MOV AX,123 CWD StoHD OutputField, Size=3, Align=left ; OutputField is now '7B??????????', DI points to the 1st '?' StoHD OutputField, Size=5 ; OutputField is now '0007B???????', DI points to the 1st '?' MOV AX,-123 CWD StoHD OutputField, Size=6, Align=Left ; OutputField is now 'FFFF85??????' StoHD OutputField, Size=2, Align=Left ; OutputField is now '85??????????' StoHD OutputField, Size=8, Align=Right ; OutputField is now 'FFFFFF85????'

    StoHD  %MACRO  Destination, Size=, Align=Right, Case=Upper, LeadingZeroes=Yes
      %StoHDflags %SETA               %cpuextLowCase  & "%Case[1]"!=="U"
      %StoHDflags %SETA %StoHDflags | %cpuextAlignL   & "%Align[1]"!=="R"
      %StoHDflags %SETA %StoHDflags | %cpuextLeading0 & "%LeadingZeroes[1]"!=="N"
      %IF "%Align[1]" == "R" && "%Size" === "" && ("%Destination" === "" || "%Destination" == "DI")
        %ERROR ID=5915, "StoHD cannot Align=Right when Size= is not specified."
        %StoHDflags %SETA %StoHDflags | %cpuextAlignL
      %ENDIF
      PUSHW %StoHDflags
      %IF "%Size" === ""
        %IF "%Destination" === "" || "%Destination == "DI"
          PUSHW -1
        %ELSE
          PUSHW SIZE# (%Destination)
        %ENDIF
      %ELSE
        PUSHW %Size
      %ENDIF
      %IF "%Destination" === ""
        PUSH DI
      %ELSE
        PUSHW %Destination
      %ENDIF
      CALL StoHD16@RT::
    StoHD16@RT:: PROC1 ; %1=Destination, %2=Size, %3=flags.
      PUSHAW
        MOV BP,SP
    %StoHDstr %SET BP-8
        SUB SP,8  ; Room for a temporary hexadecimal string.
        MOV DI,SP
        MOV BX,0x2000
        TESTW [%Par3],%cpuextLeading0
        JZ .35:
        MOV BH,'0'
    .10:TEST DX
        JNS .20:
        MOV BH,'F'
    .20:TESTW [%Par3],%cpuextLowCase
        JZ .35:
        OR BL,'x'^'X'  ; Convert 'A'..'Z'  to 'a'..'z' when BL=0x20.
    .30:OR BH,BL       ; BH is now unsignificant digit '0' or 'F' or 'f'.
    .35:MOV CX,0x0404  ; CL is nibble width, CH is nibble count.
    .40:ROL DX,CL
        MOV AX,0x000F
        AND AX,DX
        ADD AL,0x90    ; Convert the nibble in AL to a hexadigit ('0'..'F').
        DAA
        ADC AL,0x40
        DAA
        OR AL,BL       ; Adjust character case.
        MOV [SS:DI],AL
        INC DI
        DEC CH
        JNZ .40:
        MOV CH,4
        MOV DX,[%ReturnAX]
    .47:ROL DX,CL
        MOV AX,0x000F
        AND AX,DX
        ADD AL,0x90    ; Convert the nibble in AL to a hexadigit ('0'..'F').
        DAA
        ADC AL,0x40
        DAA
        OR AL,BL       ; Adjust character case.
        MOV [SS:DI],AL
        INC DI
        DEC CH
        JNZ .47:
        ; Value from DX:AX is now stored as 8 hexa digits in %StoHDstr buffer.
        MOV CL,8 ; Calculate the number of significant digits (1..8) into CX.
        MOV DX,[%ReturnDX]
        TEST DX
        JNS .50:
        NOT DX
    .50:TEST DX,0xF800
        JNZ .60:
        DEC CX
        TEST DX,0xFF80
        JNZ .60:
        DEC CX
        TEST DX,0xFFF8
        JNZ .60:
        DEC CX
        TEST DX
        JNZ .60:
        DEC CX
    .55:TEST DX
        MOV DX,[%ReturnAX]
        JNS .57:
        NOT DX
    .57:TEST DX
        JS .60:
        TEST DX,0xF800
        JNZ .60:
        DEC CX
        TEST DX,0xFF80
        JNZ .60:
        DEC CX
        TEST DX,0xFFF8
        JNZ .60:
        DEC CX
    .60:MOV AX,[%Par2] ; Size of destination buffer.
        SUB AX,CX      ; Subtract the number of significant digits.
        JB .90:        ; Destination is not long enough.
        MOV DI,[%Par1] ; Offset of Destination in segment ES.
        MOV SI,SP      ; Offset of %StoHDstr.
        ADD SI,8
        SUB SI,CX
        TESTW [%Par3],%cpuextAlignL
        JZ .80:
    .70:REP:SEGSS:MOVSB
        JMP .85:
    .80:; Right-aligned destination - store AX unsignificant characters first.
        MOV DX,CX
        XCHG AX,CX
        MOV AL,BH
        REP STOSB       ; Unsignificant digits.
        XCHG CX,DX
        REP:SEGSS:MOVSB ; Significant digits.
    .85:MOV [%ReturnDI],DI
    .90:MOV SP,BP
       POPAW
       RET 3*2
      ENDP1 StoHD16@RT::
     %ENDMACRO StoHD

     ENDHEAD cpuext16

▲Back to the top▲