在TASM中显示负数

时间:2016-07-24 08:40:13

标签: tasm

如何在tasm中打印负数?请有人帮帮我。 例如,如果我做减法2-7 =( - 5)。我怎么打印-5?

1 个答案:

答案 0 :(得分:0)

您需要自己执行数字转换(例如使用您自己的功能)。我把这个兼容TASM5的Win32应用程序作为一个例子,虽然我确信你可以在网上找到无数其他的数字>字符串转换样本。

对于这样一个简单的目标,这可能看起来像很多代码,但是如果你放置" common"设计为通用的函数足以用于包含文件或库中的任意数量的程序,真正的程序只是底部的appMain()函数,只有大约10行代码。为了成为一个完整的工作示例,我将所有内容都放在一个文件中。

您对下面显示的numToString()函数感兴趣,该函数将DWORD转换为传递给函数的缓冲区中的数字。其余代码只是一个调用该函数的框架以及一些常见的WriteConsole包装器。 numToString()支持将数字转换为任何基数(例如十六进制和二进制)。在此示例中,我传递参数以指定base-10并启用否定标志(指示将DWORD编号视为已签名)。

numToString()的算法如下:

-If number is to be interpreted as signed and IS negative, convert it to positive and set bHasSign flag
-Repeatedly divide positive (in loop) number by base (radix) and convert remainder to ASCII char; this gives us each digit of the resulting string starting with the least significant digit
-Append negative "-" to end based on bHasSign flag
-NULL terminate destination buffer
-Because the algorithm actually computes digits from least significant to most significant, we need to reverse the resultant string (including negative sign) before returning. In English, we read digits starting with most significant first.
 The reverse algorithm swaps the outer bytes, then the next inner bytes, and so on until it reaches the middle of the string.  This swapping allows us to avoid setting up a temporary buffer for the character reversal.

节目输出:

The number is: -352

计划清单:

.386
.model flat

GetStdHandle                PROTO STDCALL :DWORD
WriteConsoleA               PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
ExitProcess                 PROTO STDCALL :DWORD

getStringLength             PROTO STDCALL :DWORD
outputString                PROTO STDCALL :DWORD, :DWORD
numToString                 PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD, :DWORD

NULL                                equ     0
STD_OUTPUT_HANDLE                   equ     -11
MAXLEN_BINARYNUM_BUFFER             equ     33          ;max string length for number->string buffer assuming smallest base (32 bits) + NULL accounts for largest possible binary number
MAXLEN_BINARYNUM_BUFFER_AS_DWORD    equ     9           ;36-byte hack for MAXLEN_BINARYNUM_BUFFER used below so TASM doesn't mess up Win32 stack alignment
CODE_ERROR_BUFLEN                   equ     -10

.data

szMsg     db  'The number is: ',0

.code

;
; getStringLength() - returns zero terminated string length in eax
;
getStringLength PROC STDCALL pszString:DWORD

    ;count the characters in the string and store result in ecx
    lea esi,pszString       ;make esi point to first character in string
    mov esi,[esi]
    mov edi,esi             ;edi pointer to first character also
    cld
  @@countloop:
    lodsb
    cmp al,0
    jne SHORT @@countloop
    dec esi                 ;we're now pointing past NULL, so back up one
    sub esi,edi             ;subtract beginning pointer from pointer at NULL so that esi now contains string count

    ;return string length in returned in eax
    mov eax,esi

    ret
getStringLength ENDP

;
; outputString(pszString) - outputs a zero terminated string
;   returns 0 on failure or final character count upon success (excluding NULL terminator)
;
outputString PROC STDCALL
    LOCALS
    ARG hConsole:DWORD
    ARG pszString:DWORD
    LOCAL dwCharsWrote:DWORD

    ;get string length (in eax)
    lea esi,pszString
    mov esi,[esi]
    call getStringLength ,esi

    ;load string pointer into esi
    lea esi,pszString
    mov esi,[esi]

    ;load dwCharsWrote pointer into edx
    lea edx,dwCharsWrote

    ;write string to console
    call WriteConsoleA ,hConsole,edi,eax,edx,NULL

    ;if the function fails, return 0, otherwise return the characters written
    cmp eax,0
    jz @@funcdone
    mov eax,[dwCharsWrote]
  @@funcdone:
    ret
outputString ENDP ;outputString()

;
; numToString() - converts unsigned DWORD to digit string
;
numToString PROC STDCALL
    ARG uNum:DWORD
    ARG uBase:DWORD
    ARG pDest:DWORD
    ARG uDestLen:DWORD
    ARG bSigned:DWORD
    LOCAL pEnd:DWORD
    LOCAL bHasSign:DWORD

    ;default to number not signed
    mov [bHasSign],0

    ;if we're interpreting number as signed, see if 32nd bit (sign flag) is set
    cmp bSigned,0
    jz SHORT @@setup_div_loop
    test DWORD PTR [uNum],080000000h
    jz SHORT @@setup_div_loop
    mov [bHasSign],1    ;number is signed: set sign flag, then remove the sign
    not [uNum]          ;   from the bits VIA: bitwise NOT, then adding 1
    inc [uNum]          ;   this will give us an unsigned representation of the same number
                        ;   for which we will add a negative sign character at end

    ;setup divide/remainder loop
  @@setup_div_loop:
    mov edi,[pDest]      ;edi points to beginning of dest buffer

    mov ebx,edi          ;pEnd variable will point 1 past end of buffer
    add ebx,[uDestLen]
    mov [pEnd],ebx

    mov ebx,[uBase]      ;ebx will hold the base to convert to

    mov eax,uNum         ;eax is our number to convert

  @@divloop:
    ;range check buffer
    cmp edi,[pEnd]          ;if we are we outside of buffer?
    jae SHORT @@errorbufoverflow  ;   we've overflowed

    ;setup 32-bit divide - divide eax by radix (ebx)
    xor edx,edx
    div ebx

    ;assume remainder can always fit in byte (should range check radix)
    ;   and convert value to ASCII (values past 9 get hex digits
    cmp dl,9
    ja SHORT @@convletters
    add dl,48              ;convert 0-9 to ASCII digits
    jmp SHORT @@chardone
  @@convletters:
    add dl,55              ;convert A-F (letter A starts at 65, and we want to back it up by 10 to 55 such that 65 is for the value 10)
  @@chardone:
    mov BYTE PTR [edi],dl  ;remainder goes in buffer
    inc edi                ;point at next character
    cmp eax,0              ;if quotient nonzero, keep dividing
    jnz SHORT @@divloop

    ;
    ; loop above complete
    ;

    ;do we need to add sign?
    cmp [bHasSign],0
    jz SHORT @@nullTerminate
    ;   yes, range check that we have room
    cmp edi,[pEnd]
    jae SHORT @@errorbufoverflow
    ;   we have room, add sign character to string
    mov BYTE PTR [edi],'-'
    inc edi

  @@nullTerminate:
    ;range check buffer that we can NULL terminate the string
    cmp edi,[pEnd]
    jae SHORT @@errorbufoverflow    
    mov BYTE PTR [edi],0            ;SUCCESS - NULL terminate

    ;return character count in eax (not counting null)
    mov eax,edi         ;subtract start of buffer
    sub eax,[pDest]     ;   from ending position of buffer to obtain count

    ;
    ; we now have correct numeric string, but with least significant digits first
    ; we must reverse the string to make it human-readible
    ;
    mov esi,[pDest]  ;esi is left pointer
    dec edi          ;edi is right pointer
  @@reverseloop:
    cmp esi,edi
    jae SHORT @@procdone   ;if esi ever meets or goes past edi, we're done!

    mov dl,BYTE PTR [esi]    ;swap esi and edi bytes
    xchg dl,BYTE PTR [edi]
    mov BYTE PTR [esi],dl

    dec edi
    inc esi
    jmp SHORT @@reverseloop    

  @@errorbufoverflow:
    ;ERROR: buffer length too small
    mov eax,CODE_ERROR_BUFLEN

  @@procdone:
    ret
numToString ENDP

;
; appMain()
;
appMain PROC STDCALL
    LOCAL hConsole:DWORD
    LOCAL szCode[MAXLEN_BINARYNUM_BUFFER_AS_DWORD]:DWORD

    ;get handle to console
    call GetStdHandle ,STD_OUTPUT_HANDLE
    mov hConsole,eax

    ;output message
    call outputString ,hConsole,OFFSET szMsg

    ;this is your negative value
    mov eax,-352

    ;convert value to string
    lea esi,[szCode]
    call numToString ,eax,10,esi,MAXLEN_BINARYNUM_BUFFER,1

    ;output number buffer
    lea esi,[szCode]
    call outputString ,hConsole,esi
    ret
appMain ENDP

;
; Program Entry Point
;
_start:
    call appMain
    call ExitProcess ,eax
end _start