печать чисел 64бит

 

Нашел в интернетах один код печати 64битного числа:

org 100h
use16
mov edi,buff
mov eax,$FFFFFFFF
mov edx,$FFFFFFFF
    ; edx:eax = number to convert, edi = buffer to output (at least 20B)
    ; returns edi pointing after last character
    push    eax
    push    edx
    push    esi
    push    ebx
    push    ebp
    push    ecx
    ; test for zero in edx:eax -> special handling
    mov     esi,edx
    or     esi,eax
    jz     .zeroNumber
    ; convert other numbers by subtracting 10^k powers
    mov     esi,pow10table-8
.skipLeadingZero:
    add     esi,8
    cmp     edx,[esi+4]
    jc     .skipLeadingZero
    jne     .next10powerInit
    cmp     eax,[esi]
    jc     .skipLeadingZero
    jmp     .next10powerInit
    ; since here every power of 10 is counted and set into output
.next10power:
    mov     [edi],cl ; write counter digit of previous 10th power
    inc     edi
.next10powerInit:
    mov     ebx,[esi]
    mov     ebp,[esi+4] ; ebp:ebx = 10^k
    test    ebx,ebx
    jz     .finish ; only zero terminator can have lower 32b == 0
    mov     cl,'0'
    add     esi,8
.compare10power:
    cmp     edx,ebp
    jc     .next10power
    jnz     .sub10power
    cmp     eax,ebx
    jc     .next10power
.sub10power:
    sub     eax,ebx
    sbb     edx,ebp
    inc     cl
    jmp     .compare10power
.zeroNumber:
    mov     [edi],byte '0'
    inc     edi
.finish:
mov byte [edi],'$'
push cs
pop ds
mov dx,buff
mov ax,0924h
int 21h
    pop     ecx
    pop     ebp
    pop     ebx
    pop     esi
    pop     edx
    pop     eax
    ret

pow10table:
    dq 10000000000000000000
    dq 1000000000000000000
    dq 100000000000000000
    dq 10000000000000000
    dq 1000000000000000
    dq 100000000000000
    dq 10000000000000
    dq 1000000000000
    dq 100000000000
    dq 10000000000
    dq 1000000000
    dq 100000000
    dq 10000000
    dq 1000000
    dq 100000
    dq 10000
    dq 1000
    dq 100
    dq 10
    dq 1
    dq 0 ; terminator
buff:

JinX предложил свой вариант:

org 100h
use16
num_size        =       1024
word_count      =       (num_size+15)/16
                ; Делим
                mov     ebx,$FFFFFFFF
                mov     ebp,$7FFFFFFF;ebx         ; ebp:ebx = 64-битное число 18446744073709551615
                xor     cx,cx           ; кол-во цифр числа
                mov     edi,10          ; делитель
        @@:     xor     edx,edx
                xchg    eax,ebp         ; eax = ebp (старшая часть)
                div     edi             ; eax = частное, edx = остаток
                mov     ebp,eax         ; записываем старшую часть в eax
                xchg    eax,ebx         ; eax = ebx (младшая часть)
                div     edi             ; eax = частное, edx = остаток
                mov     ebx,eax         ; записываем младшую часть в ebx
                push    dx              ; цифра
                inc     cx
                or      eax,ebp         ; результат не нулевой?
                jnz     @B
                ; Выводим
                mov     ah,2
        @@:     pop     dx
                add     dl,'0'
                int     $21
                loop    @B
                ret

обычное деление пары edx:eax не прокатило.

Дополнение 19-02-2022 Нашел сайт, где есть немало интересных статей:

Для преобразования и форматированного вывода 32-битных значений в ассемблере используется стандартная API-функция wsprintf, но она бесполезна при работе с большими числами, например QWORD (64 бита). Значит обойдемся совсем без использования API.
;---------------------------------------------------
; Преобразование QWORD в десятичное число
; Параметры вызова:
; dwHigh - Старшее двойное слово
; dwLow  - Младшее двойное слово
; lpBuff - указатель на буфер-приемник
;---------------------------------------------------
proc    bignum dwHigh:DWORD, dwLow:DWORD, lpBuff:DWORD
        pushad                   ; Сохранить все регистры
        mov     eax,[dwLow]      ; Младшее двойное слово
        mov     edx,[dwHigh]     ; Старшее двойное слово
        mov     edi,[lpBuff]     ; Указатель на буфер-приемник
  
        xchg    esi,edx          ; Сохранить старший dword
        mov     ebx,10           ; Основание системы счисления
        xor     ecx,ecx          ; Счетчик десятичных цифр
.bignum_1:
        xchg    eax,esi          ; Расчитать десятичную цифру
        xor     edx,edx
        div     ebx
        xchg    esi,eax
        div     ebx
        or      dl,'0'           ; Преобразовать результат в символ цифры
        push    edx              ; Сохранить цифру в стеке
        inc     ecx              ; Увеличить счетчик цифр
        or      eax,eax          ; Все преобразовали?
        jnz     .bignum_1
  
.bignum_2:
        pop     eax              ; Записать все цифры из стека в буфер
        stosb
        loop    .bignum_2
        xor     eax,eax          ; Признак окончания строки
        stosb
        popad                    ; Восстановить все регистры
        ret                      ; Ворзврат из процедуры
endp

Пример использования:
; Сегмент данных
section '.data' data readable writeable
...
buff rb 100h
  
; Сегмент кода
section '.code' code readable executable
...
        mov     edx,70655326h  ; EDX:EAX = 7065532612345678h
        mov     eax,12345678h  ; в десятичной системе это 8098970927874987640
        stdcall bignum, edx, eax, buff
        ; После выполнения буфер содержит строку "8098970927874987640"
Функция получилась достаточно универсальной. Для преобразования числа в другую систему счисления с основанием, отличным от 10, достаточно изменить в коде функции mov ebx,10 на нужное значение. Или, как вариант, ввести дополнительный параметр вызова функции, определяющий основание.



Комментарии