assembly 如何在汇编中的EDX:EAX中打印乘法结果

yv5phkfx  于 2023-04-12  发布在  其他
关注(0)|答案(1)|浏览(138)

当在汇编中对32位数字进行乘法时,结果将被放入EDX:EAX组合中。结果的上半部分进入EDX,下半部分进入EAX。如果EDX和EAX都有结果的两部分,我如何使用Irvine32位库将这些值打印到屏幕上?请参阅示例代码和注解:

.386
.model flat, stdcall
.stack 4096
ExitProcess proto, dwExitCode:dword
include Irvine32.inc

.data
    num1 dword 1000000
    num2 dword 1000000
    temp dword ?
    full_result qword ?
.code
main proc
    mov eax, num1
    mul num2 ;Result will be put in EDX:EAX (Upper half of number and Lower half of number)
    ;EDX has the value 232 in decimal. 000000E8 in hex
    ;EAX has the value 3567587328 in decimal. D4A51000 in hex
    ;When you put these numbers togather, you get 000000E8D4A51000 in hex. 
    ;When you convert these numbers back to its decimal representation, we get the correct value of 1000000000000

    ;How to display the result into the screen using Irvine32 library (not 64)
    mov temp, eax
    mov eax, edx ;Put the upper half of result in eax
    call WriteDec ;Write the value in eax
    mov eax, temp ;Put the lower half of result in eax
    call WriteDec

    ;This will prints out 2323567587328 instead of 1000000000000

invoke ExitProcess, 0
main endp
end main

有没有办法把这个数字2323567587328转换成不同的形式,这样我就可以正确地显示上半部分和下半部分?(打包BCD等。)
如果无法将这个数字格式化为1000000000000,那么请告诉我如何将这个值赋给full_result qword类型变量。

iyfjxgzm

iyfjxgzm1#

mov eax, num1
mul num2

mul指令在EDX:EAX中生成一个 * 无符号 * 64位乘积。
下面的代码将EDX:EAX中的 unsigned 64位数字转换为十进制表示。然后可以使用Irvine的WriteString函数输出字符串。

转换EDX:EAX中的 unsigned 64位数

在x86上,需要2个除法的级联来将EDX:EAX中的64位值除以10。
第一次除法除以高被除数(用0扩展)得到一个高商。第二次除法除以低被除数(用第一次除法的余数扩展)得到一个低商。我们将第二次除法的余数保存在堆栈中。
为了检查EDX:EAX中的qword是否为零,我在临时寄存器中对两个半部分进行了OR运算。
我选择在堆栈上放置一个sentinel,而不是计数数字,因为这个sentinel得到一个值(10),没有数字可以得到([0,9]),它很好地允许确定存储循环何时必须停止。

.data
    num1   dword 1000000
    num2   dword 1000000
    Buffer byte 32 dup(0)
.code
main proc
    mov     eax, num1
    mul     num2

    push    ebx
    push    edi
    mov     edi, OFFSET Buffer ; Begin of the buffer
    mov     ebx, 10        ; CONST
    push    ebx            ; Sentinel
.a: mov     ecx, eax       ; Temporarily store LowDividend in ECX
    mov     eax, edx       ; First divide the HighDividend
    xor     edx, edx       ; Setup for division EDX:EAX / EBX
    div     ebx            ; -> EAX is HighQuotient, Remainder is re-used
    xchg    eax, ecx       ; Temporarily move it to ECX restoring LowDividend
    div     ebx            ; -> EAX is LowQuotient, Remainder EDX=[0,9]
    push    edx            ; (1) Save remainder for now
    mov     edx, ecx       ; Build true 64-bit quotient in EDX:EAX
    or      ecx, eax       ; Is the true 64-bit quotient zero?
    jnz     .a             ; No, use as next dividend

    pop     eax            ; (1a) First pop (Is digit for sure)
.b: add     eax, "0"       ; Turn into character [0,9] -> ["0","9"]
    stosb                  ; Store in buffer
    pop     eax            ; (1b) All remaining pops
    cmp     eax, ebx       ; Was it the sentinel?
    jb      .b             ; Not yet
    mov     BYTE PTR [edi], 0 ; Irvine32 requires zero-termination
    pop     edi
    pop     ebx

    mov     edx, OFFSET Buffer
    call    WriteString

转换EDX:EAX中的 * 有符号 * 64位数

程序如下:
首先通过测试符号位来确定有符号数是否为负。
如果是,则对数字求反并输出一个“-”字符。
代码段的其余部分与无符号数相同。

mov     edi, OFFSET Buffer ; Begin of the buffer
    test    edx, edx       ; Sign bit is bit 31 of high dword
    jns     .u             ; It's a positive number
    neg     edx            ; |
    neg     eax            ; | Negate EDX:EAX
    sbb     edx, 0         ; |
    mov     BYTE PTR [edi], "-"
    inc     edi
.u: mov     ebx, 10        ; CONST
    push    ebx            ; Sentinel
.a:
    ...

上面的代码片段基于我的16位Q/A Displaying numbers with DOS。你也可以阅读一些额外的解释...

当你不关心字符串总是从相同的已知地址开始时的替代方法

这个版本更短更快。

.data
    num1   dword 1000000
    num2   dword 1000000
    Buffer byte 32 dup(0)
.code
main proc
    mov     eax, num1
    mul     num2

    push    ebx
    push    edi
    mov     edi, OFFSET Buffer+31 ; End of the buffer
    mov     BYTE PTR [edi], 0 ; Irvine32 requires zero-termination

    mov     ebx, 10        ; CONST
.a: mov     ecx, eax       ; Temporarily store LowDividend in ECX
    mov     eax, edx       ; First divide the HighDividend
    xor     edx, edx       ; Setup for division EDX:EAX / EBX
    div     ebx            ; -> EAX is HighQuotient, Remainder is re-used
    xchg    eax, ecx       ; Temporarily move it to ECX restoring LowDividend
    div     ebx            ; -> EAX is LowQuotient, Remainder EDX=[0,9]
    dec     edi
    add     edx, "0"       ; Turn into character [0,9] -> ["0","9"]
    mov     [edi], dl      ; Store in buffer
    mov     edx, ecx       ; Build true 64-bit quotient in EDX:EAX
    or      ecx, eax       ; Is the true 64-bit quotient zero?
    jnz     .a             ; No, use as next dividend
    mov     edx, edi       ; -> EDX is address of ASCIIZ string
    pop     edi
    pop     ebx

    call    WriteString

相关问题