如果我们在寄存器中有一个数字,我们需要得到它的十进制数字值——这很容易。我们所需要的只是将数字除以 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
。
那么遇到这种情况我该怎么办呢?
在 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.
程序如下:
首先通过测试符号位来判断有符号数是否为负数
如果是,则将数字取反并输出一个“-”字符。
该片段的其余部分与无符号数相同。
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.