不适合 NASM 中的一个寄存器的数字的十进制表示

问题描述 投票:0回答:1

如果我们在寄存器中有一个数字,我们需要得到它的十进制数字值——这很容易。我们所需要的只是将数字除以 10 并保存我们得到的余数。

lp:     cmp     eax, 0  ; have we finished?
        je      prinrt
        inc     ecx
        div     dword [divider]
        mov     [result + ecx - 1], dl
        xor     edx, edx
        jmp     lp

但是如果一个数字对于一个寄存器来说太大了怎么办?假设有一条指令

mul ebx
并且最终数字太大而无法容纳 32 位。现在结果编号写成
edx:eax
。 那么遇到这种情况我该怎么办呢?

assembly x86 nasm integer-division
1个回答
0
投票

EDX:EAX中保存的unsigned 64位数字的转换

在 x86 上,需要级联 2 个除法来将 EDX:EAX 中的 64 位值除以 10.
第一除法除以高股息(用 0 扩展)产生高商。第二个除法除以低股息(用第一个除法的余数扩展)产生低商。这是我们保存在堆栈中的第二部分的剩余部分。

为了检查 EDX:EAX 中的 qword 是否为零,我在临时寄存器中对两半进行了或运算。

我没有计算数字,需要寄存器,而是选择在堆栈上放置一个哨兵。因为这个哨兵得到一个值 (10),任何数字都不可能有 ([0,9]),所以它很好地允许确定存储循环何时必须停止。

    mov     edi, 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     edi, Buffer+32 ; End of the buffer
    mov     ebx, 10        ; CONST
.c: 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
    mov     edx, ecx       ; Build true 64-bit quotient in EDX:EAX
    or      ecx, eax       ; Is the true 64-bit quotient zero?
    jnz     .c             ; No, use as next dividend

; EDI now points at the left-most digit of the 64-bit number.

EDX:EAX中保存的signed 64位数字的转换

程序如下:

首先通过测试符号位来判断有符号数是否为负数
如果是,则将数字取反并输出一个“-”字符。

该片段的其余部分与无符号数相同。

    mov     edi, 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 [edi], "-"
    inc     edi
.u: mov     ebx, 10        ; CONST
    push    ebx            ; Sentinel
.a:
    ...

又一个不使用堆栈的替代方案:

    test    edx, edx       ; Sign bit is bit 31 of high dword
    pushf                  ; (***)
    jns     .u             ; It's a positive number
    neg     edx            ; |
    neg     eax            ; | Negate EDX:EAX
    sbb     edx, 0         ; |
.u: mov     edi, Buffer+32 ; End of the buffer
    mov     ebx, 10        ; CONST
.c:
    ...

    popf                   ; (***)
    jns     .z             ; It's a positive number
    dec     edi
    mov     byte [edi], "-"
.z:

; EDI now points at the left-most digit (or the sign) of the 64-bit number.
© www.soinside.com 2019 - 2024. All rights reserved.