Преобразование 64-битного числа в строку

Автор: xrnd | Рубрика: Исходники | 27-12-2010 | Распечатать запись Распечатать запись

В части 22 учебного курса рассматривались способы преобразования чисел в строку. Однако, вывести в десятичном виде число больше 32 бит не так просто. Сложность в том, что требуется делить число на 10, а число слишком большое и в регистры DX:AX не помещается.

К счастью, в программе на ассемблере можно делить числа любой разрядности 🙂 Правда, одной командой DIV тут не обойтись. Придётся реализовать специальный алгоритм.

Вот код примера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use16                       ;Генерировать 16-битный код
org 100h                    ;Программа начинается с адреса 100h
    jmp start               ;Перепрыгнуть данные
;-------------------------------------------------------------------------------
; Данные
a       dq 1600000000000000
b       dq 33000000
c       dq 12345678901234567890
s_a     db 'a = $'
s_b     db 'b = $'
s_c     db 'c = $'
endline db 13,10,'$'
s_pak   db 'Press any key...$'
;-------------------------------------------------------------------------------
; Код
start:
    mov di,s_a
    call print_str          ;Вывод строки 'a = '
    mov bx,a
    call print_qword_udec   ;Вывод числа a
    call print_endline      ;CR+LF
 
    mov di,s_b
    call print_str          ;Вывод строки 'b = '
    mov bx,b
    call print_qword_udec   ;Вывод числа b
    call print_endline      ;CR+LF
 
    mov di,s_c
    call print_str          ;Вывод строки 'c = '
    mov bx,c
    call print_qword_udec   ;Вывод числа c
    call print_endline      ;CR+LF
 
    mov di,s_pak
    call print_str          ;Вывод строки 'Press any key...'
    mov ah,8                ;\
    int 21h                 ;/ Ввод символа без эха
    mov ax,4C00h            ;\
    int 21h                 ;/ Завершение программы
 
;-------------------------------------------------------------------------------
;Процедура вывода учетверённого слова на консоль в десятичном виде (без знака)
; BX - адрес qword
print_qword_udec:
    push di
    sub sp,22               ;Выделение места в стеке для буфера
    mov di,sp               ;DI = адрес буфера
    push word[bx+6]         ;\
    push word[bx+4]         ; \
    push word[bx+2]         ; / Помещение qword в стек
    push word[bx]           ;/
    call qword_to_udec_str  ;Преобразование qword в строку
    mov byte[di],'$'        ;Добавление символа конца строки
    mov di,sp               ;DI = адрес буфера со строкой
    call print_str          ;Вывод строки на консоль
    add sp,22               ;Восстановление указателя стека
    pop di
    ret
 
;-------------------------------------------------------------------------------
;Процедура преобразования учетверённого слова
;в строку в десятичном виде (без знака)
;Вход: qword в стеке (младший байт по младшему адресу!)
;  DI - буфер для строки (20 символов). Значение регистра не сохраняется.
qword_to_udec_str:
    push bp                 ;Сохранение BP
    mov bp,sp               ;BP=SP
    push ax                 ;Сохранение используемых регистров
    push cx
    push bx
    push si
 
    xor cx,cx               ;Обнуление CX
    mov bx,10               ;В BX делитель (10 для десятичной системы)
 
.lp1:                       ;Цикл получения остатков от деления
    xor dx,dx
    mov si,8                ;SI = смещение слова для деления
 
.div64:                     ;Цикл 64-битного деления
    mov ax,[bp+si+2]        ;Загрузка слова в AX
    div bx                  ;AX = (DX:AX)/10; DX = остаток
    mov [bp+si+2],ax        ;Сохранение результата деления слова
    sub si,2                ;Уменьшение смещения на 2
    jnz .div64              ;Переход к началу цикла, если SI != 0
 
    add dl,'0'              ;Преобразование остатка в код символа
    push dx                 ;Сохранение в стеке
    inc cx                  ;Увеличение счетчика символов
    or ax,[bp+6]            ;\
    or ax,[bp+8]            ; > Проверка частного на равенство 0 ;)
    or ax,[bp+10]           ;/
    jnz .lp1                ;Переход к началу цикла, если частное не 0.
 
.lp2:                       ;Цикл извлечения символов из стека
    pop dx                  ;Восстановление символа из стека
    mov [di],dl             ;Сохранение символа в буфере
    inc di                  ;Инкремент адреса буфера
    loop .lp2               ;Команда цикла
 
    pop si                  ;Восстановление сохранённых регистров
    pop bx
    pop cx
    pop ax
    leave                   ;Восстановление SP и BP
    ret 8                   ;Возврат с выталкиванием 8 байт из стека
 
;-------------------------------------------------------------------------------
;Процедура вывода строки на консоль
; DI - адрес строки
print_str:
    push ax
    mov ah,9                ;Функция DOS 09h - вывод строки
    xchg dx,di              ;Обмен значениями DX и DI
    int 21h                 ;Обращение к функции DOS
    xchg dx,di              ;Обмен значениями DX и DI
    pop ax
    ret
 
;-------------------------------------------------------------------------------
;Процедура вывода конца строки (CR+LF)
print_endline:
    push di
    mov di,endline          ;DI = адрес строки с символами CR,LF
    call print_str          ;Вывод строки на консоль
    pop di
    ret

Я сделал деление в виде цикла. Суть алгоритма в следующем. Деление qword выполняется в 4 этапа. Сначала обнуляем DX, загружаем старшее слово делимого и делим. Результат оказывается в AX, а в DX — остаток. Сохраняем результат на место старшего слова. Затем загружаем в AX следующее слово и делим. Обратите внимание, что в DX остаток от предыдущего деления! Снова сохраняем результат и загружаем следующее слово. На четвёртый раз частное оказывается на месте делимого, а остаток в DX 🙂

Буфер для строки выделяется в стеке. Максимум может быть 20 цифр + конец строки. 64-битное значение тоже помещается в стек и используется для хранения промежуточных результатов деления.

Скриншот программы:

Комментарии: