EuroAssembler Index Manual Download Source Macros


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 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 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 Fs 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 Fs 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▲