首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在TASM中显示负数

在TASM中显示负数
EN

Stack Overflow用户
提问于 2016-07-24 16:40:13
回答 1查看 423关注 0票数 0

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

EN

回答 1

Stack Overflow用户

发布于 2016-08-27 03:27:22

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

对于这样一个简单的目标,这看起来似乎有很多代码,但是,如果您将“通用”函数放在一个包含文件或库中,这些函数足够通用,可以在任意数量的程序中使用,那么真正的程序只有底部的appMain()函数,它只有大约10行代码。为了成为一个完整的工作示例,我将所有内容都放在一个文件中。

您对如下所示的numToString()函数很感兴趣,该函数将DWORD值转换为传递给该函数的缓冲区中的数字。代码的其余部分只是一个框架,用于调用函数以及一些常见的WriteConsole包装器。numToString()支持将数字转换为任意基数(例如十六进制和二进制)。在本例中,我传递参数以指定base-10,并启用负标志(指示将DWORD数字视为有符号)。

numToString()的算法如下:

代码语言:javascript
复制
-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.

程序输出:

代码语言:javascript
复制
The number is: -352

程序列表:

代码语言:javascript
复制
.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
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38550193

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档