Форум программистов, компьютерный форум, киберфорум
Assembler для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.99/849: Рейтинг темы: голосов - 849, средняя оценка - 4.99
8 / 8 / 0
Регистрация: 03.10.2009
Сообщений: 3
1

Ввод и вывод чисел в различных системах счисления

06.10.2009, 14:14. Просмотров 178154. Ответов 11

Нужно выполнить перевод чисел из десятичной в двоичную систему счисления. Дать их внутреннее (машинное) представление в соответствии с диапазоном в знаковых и беззнаковых форматах типов Shortlnt (signed char), Byte (unsigned char), Integer (int), Word (unsigned int). Машинное представление данных должно быть дано в двоичной и шестнадцатеричной системах счисления. Создать программу на языке Ассемблер обеспечивающих хранение данных в регистрах микропроцессора и операции пересылки данных.

люди помогите пожалуйста
8
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
06.10.2009, 14:14
Ответы с готовыми решениями:

Ввод и вывод чисел в различных системах счисления
здравствуйте. помогите разработать программу Разработать программу перевода ввода и вывода чисел в...

Ввод и вывод чисел в различных системах счисления, исправить код
.model small .stack 100h .data .code InputInt proc mov ah,0ah

Ввод символов и отображение в различных системах счисления
Здравствуйте, форумчане! Есть проблема, прошу подсказки. Нужно создать прогу на ассемблере,...

Организовать ввод значений в различных системах счисления
Всем привет, делаю калькулятор и есть некоторые трудности! В общем, шкодю в WPF ХAML C#, и пытаюсь...

11
2530 / 826 / 10
Регистрация: 31.05.2009
Сообщений: 1,669
08.10.2009, 21:42 2
Вывод целого знакового, либо беззнакового числа.
Число для вывода должно находиться в ax.
Изменяя число - основание сс можно выводить числа в двоичной, троичной и т.п. системах счисления
Assembler
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
OutInt proc
        
;; если число знаковое, то необходимо расскоментировать следующие строки
;; Проверяем число на знак.
;   test    ax, ax
;   jns     oi1
;
;; Если оно отрицательное, выведем минус и оставим его модуль.
;   mov  cx, ax
;   mov     ah, 02h
;   mov     dl, '-'
;   int     21h
;   mov  ax, cx
;   neg     ax
;; Количество цифр будем держать в CX.
;oi1:  
    xor     cx, cx
    mov     bx, 10 ; основание сс. 10 для десятеричной и т.п.
oi2:
    xor     dx,dx
    div     bx
; Делим число на основание сс. В остатке получается последняя цифра.
; Сразу выводить её нельзя, поэтому сохраним её в стэке.
    push    dx
    inc     cx
; А с частным повторяем то же самое, отделяя от него очередную
; цифру справа, пока не останется ноль, что значит, что дальше
; слева только нули.
    test    ax, ax
    jnz     oi2
; Теперь приступим к выводу.
    mov     ah, 02h
oi3:
    pop     dx
; Извлекаем очередную цифру, переводим её в символ и выводим.
;; раскоментировать если основание сс > 10, т.е. для вывода требуются буквы
;   cmp     dl,9
;   jbe     oi4
;   add     dl,7
;oi4:
    add     dl, '0'
    int     21h
; Повторим ровно столько раз, сколько цифр насчитали.
    loop    oi3
    
    ret
 
OutInt endp
Вывод целого беззнакового числа от 0 до 99.
Число для вывода должно находиться в ax.
Assembler
1
2
3
4
5
6
7
8
9
10
OutInt proc
    aam 
    add ax,3030h 
    mov dl,ah 
    mov dh,al 
    mov ah,02 
    int 21h 
    mov dl,dh 
    int 21h
OutInt endp
Вывод числа в шестнадцатеричной системе
Число для вывода должно находиться в ax.
Результат помещяется в es:di.

Assembler
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
byte2hex        proc    near
                push    cx
                mov     cx,2
@@L1:           rol     dl,4
                mov     ax,300fh
                and     al,dl
                aaa
                aad     11h
                stosb
                loop    @@L1
                pop     cx
                ret
byte2hex        endp
 
word2hex        proc    near
                push    cx
                mov     cx,2
@@L1:           rol     dx,8
                call    byte2hex
                loop    @@L1
                pop     cx
                ret
word2hex        endp
 
dword2hex       proc    near
                mov     cx,2
@@L1:           rol     edx,16
                call    word2hex
                loop    @@L1
                ret
dword2hex       endp
Вывод числа в двоичной системе счисления.
Число для вывода должно находиться в ax.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
OutBin proc
 
    mov bx,ax
    mov cx,16
ob1:
    shl bx,1
    jc ob2
    
    mov dl,'0'
    jmp ob3
    
ob2:
    mov dl,'1'
ob3:
    mov ah,2
    int 21h
    loop ob1
    
OutBin endp
Ввод целого положительного или отрицательного числа числа.
Введенное число будет находиться в ax
Изменяя число - основание сс, можно вводить числа в различных системах до десятеричной.
Assembler
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
InputInt proc 
 
    mov ah,0ah
    xor di,di
    mov dx,offset buff ; аддрес буфера
    int 21h ; принимаем строку
    mov dl,0ah
    mov ah,02
    int 21h ; выводим перевода строки
    
; обрабатываем содержимое буфера
    mov si,offset buff+2 ; берем аддрес начала строки
    cmp byte ptr [si],"-" ; если первый символ минус
    jnz ii1
    mov di,1  ; устанавливаем флаг
    inc si    ; и пропускаем его
ii1:
    xor ax,ax
    mov bx,10  ; основание сc
ii2:
    mov cl,[si] ; берем символ из буфера
    cmp cl,0dh  ; проверяем не последний ли он
    jz endin
    
; если символ не последний, то проверяем его на правильность
    cmp cl,'0'  ; если введен неверный символ <0
    jb er
    cmp cl,'9'  ; если введен неверный символ >9
    ja er
 
    sub cl,'0' ; делаем из символа число 
    mul bx     ; умножаем на 10
    add ax,cx  ; прибавляем к остальным
    inc si     ; указатель на следующий символ
    jmp ii2     ; повторяем
 
er:   ; если была ошибка, то выводим сообщение об этом и выходим
    mov dx, offset error
    mov ah,09
    int 21h
    int 20h
 
; все символы из буфера обработаны число находится в ax
endin:
    cmp di,1 ; если установлен флаг, то
    jnz ii3
    neg ax   ; делаем число отрицательным
ii3:
    ret
 
error db "incorrect number$"
buff    db 6,7 Dup(?)
InputInt endp
75
2011 / 1283 / 60
Регистрация: 05.06.2010
Сообщений: 2,213
12.12.2010, 14:48 3
В дополнение к теме:
Вывод вещественного числа
Число для вывода должно находиться на вершине стека сопроцессора,
функции должен передаваться через стек параметр - число цифр после запятой(из-за ошибок округления его обычно нельзя посчитать заранее)
Assembler
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
.286C
.model small
.data
 
flt_num dq      -324.7341234781
 
.code
start:
mov ax, @data
mov ds, ax
 
finit
fld flt_num
push 10
call outfloat
 
 
.exit
 
; Вывод вещественного числа
; аргумент - количество цифр дробной части
length_frac     equ     [bp+4]
; локальные переменные
ten     equ word ptr [bp-2]
temp    equ word ptr [bp-4]
 
OutFloat proc   near
        enter   4, 0            ; пролог - выделим в кадре стека 4 байта под локальные переменные
        mov     ten, 10
        ftst                    ; определяем знак числа
        fstsw   ax
        sahf
        jnc     @positiv
        mov     al, '-'         ; если число отрицательное - выводим минус
        int     29h
        fchs                    ; и получаем модуль числа
@positiv:
        fld1                    ; загружаем единицу
        fld     st(1)           ; копируем число на вершину стека
        fprem                   ; выделим дробную часть
        fsub    st(2), st       ; отнимем ее от числа - получим целую часть
        fxch    st(2)           ; меняем местами целую и дробную части
        xor     cx, cx          ; обнуляем счетчик
; далее идет стандартный алгоритм вывода целого числа на экран
@1:
        fidiv   ten             ; делим целую часть на десять
        fxch    st(1)           ; обменяем местами st и st(1) для команды fprem
        fld     st(1)           ; копируем результат на вершину стека 
        fprem                   ; выделим дробную часть (цифру справа от целой части)
        fsub    st(2), st       ; получим целую часть
        fimul   ten             ; *10
        fistp   temp            ; получаем очередную цифру      
        push    temp            ; заталкиваем ее глубже в стек
        inc     cx              ; и увеличим счетчик
        fxch    st(1)           ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1)
        ftst                    ; проверим не получили ли в частном 0?
        fstsw   ax
        sahf
        jnz     @1              ; нет - продолжим цикл
@2:                             ; извлекаем очередную цифру, переводим её в символ и выводим.
        pop     ax
        add     al, '0'
        int     29h
        loop    @2
; далее то же самое, только для дробной части. Алгоритм похож на вывод целого числа, только вместо деления умножение и проход по числу слева
        fstp    st              ; сначала проверим, есть ли дробная часть
        fxch    st(1)
        ftst
        fstsw   ax
        sahf
        jz      @quit           ; дробная часть отсутствует
        mov     al, '.'
        int     29h             ; если присутствует - выведем точку
        mov     cx, length_frac ; помещаем в счетчик длину дробной части
@3:
        fimul   ten             ; умножим на 10
        fxch    st(1)           ; подготовка для fprem - меняем st и st(1) местами и
        fld     st(1)           ; копируем число на вершину
        fprem                   ; отделим дробную часть от целой
        fsub    st(2), st       ; и оставляем дробную
        fxch    st(2)
        fistp   temp            ; выталкиваем полученное число из стека в temp
        mov     ax, temp        ; по дробной части идем слева, значит число выводим сразу, без предварительного сохранения в стек
        or      al, 30h         ; перевод в ascii
        int     29h             ; на экран
        fxch    st(1)           ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1)
        ftst
        fstsw   ax
        sahf                    ; проверим на 0 остаток дробной части
        loopne  @3
@quit:
        fstp                    ; готово. Чистим стек сопроцессора
        fstp    st
        leave                   ; эпилог
        ret     2
OutFloat endp
 
end start
45
Ушел с форума
Автор FAQ
14152 / 7040 / 827
Регистрация: 11.11.2010
Сообщений: 12,647
14.03.2013, 04:57 4
вывод числа из сопроцессора на экран
  1. Код vital792, взято здесь. Число для вывода должно находиться на вершине стека сопроцессора, функции должен передаваться через стек параметр - число цифр после запятой(из-за ошибок округления его обычно нельзя посчитать заранее)
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    .286C
    .model small
    .data 
    flt_num dq      -324.7341234781 
    .code
    start: mov ax, @data
    mov ds, ax 
    finit
    fld flt_num
    push 10
    call outfloat 
    .exit 
    ; Вывод вещественного числа, аргумент - количество цифр дробной части
    length_frac     equ     [bp+4]
    ; локальные переменные
    ten     equ word ptr [bp-2]
    temp    equ word ptr [bp-4]
     
    OutFloat proc   near
            enter   4, 0            ; пролог - выделим в кадре стека 4 байта под локальные переменные
            mov     ten, 10
            ftst                    ; определяем знак числа
            fstsw   ax
            sahf
            jnc     @positiv
            mov     al, '-'         ; если число отрицательное - выводим минус
            int     29h
            fchs                    ; и получаем модуль числа
    @positiv:        fld1                    ; загружаем единицу
            fld     st(1)           ; копируем число на вершину стека
            fprem                   ; выделим дробную часть
            fsub    st(2), st       ; отнимем ее от числа - получим целую часть
            fxch    st(2)           ; меняем местами целую и дробную части
            xor     cx, cx          ; обнуляем счетчик
    ; далее идет стандартный алгоритм вывода целого числа на экран
    @1:        fidiv   ten             ; делим целую часть на десять
            fxch    st(1)           ; обменяем местами st и st(1) для команды fprem
            fld     st(1)           ; копируем результат на вершину стека 
            fprem                   ; выделим дробную часть (цифру справа от целой части)
            fsub    st(2), st       ; получим целую часть
            fimul   ten             ; *10
            fistp   temp            ; получаем очередную цифру      
            push    temp            ; заталкиваем ее глубже в стек
            inc     cx              ; и увеличим счетчик
            fxch    st(1)           ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1)
            ftst                    ; проверим не получили ли в частном 0?
            fstsw   ax
            sahf
            jnz     @1              ; нет - продолжим цикл
    @2:     pop     ax; извлекаем очередную цифру, переводим её в символ и выводим.        
            add     al, '0'
            int     29h
            loop    @2
    ; далее то же самое, только для дробной части. Алгоритм похож на вывод целого числа, только вместо деления умножение и проход по числу слева
            fstp    st              ; сначала проверим, есть ли дробная часть
            fxch    st(1)
            ftst
            fstsw   ax
            sahf
            jz      @quit           ; дробная часть отсутствует
            mov     al, '.'
            int     29h             ; если присутствует - выведем точку
            mov     cx, length_frac ; помещаем в счетчик длину дробной части
    @3:        fimul   ten             ; умножим на 10
            fxch    st(1)           ; подготовка для fprem - меняем st и st(1) местами и
            fld     st(1)           ; копируем число на вершину
            fprem                   ; отделим дробную часть от целой
            fsub    st(2), st       ; и оставляем дробную
            fxch    st(2)
            fistp   temp            ; выталкиваем полученное число из стека в temp
            mov     ax, temp        ; по дробной части идем слева, значит число выводим сразу, без предварительного сохранения в стек
            or      al, 30h         ; перевод в ascii
            int     29h             ; на экран
            fxch    st(1)           ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1)
            ftst
            fstsw   ax
            sahf                    ; проверим на 0 остаток дробной части
            loopne  @3
    @quit:        fstp                    ; готово. Чистим стек сопроцессора
            fstp    st
            leave                   ; эпилог
            ret     2
    OutFloat endp 
    end start
  2. автор Mikl___
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    .686
    .model flat
    include windows.inc
    includelib user32.lib
    extern _imp__MessageBoxA@16:dword
    .code
    start:  fld x
            xor ebx,ebx
            mov edi,offset String
            fldz
            fcomip st,st(1) ;сравниваю число с нулем
            jnz @f
            mov byte ptr [edi],'0'
            jmp b0
    @@:     jb @f
            mov al,'-'      ;если отрицательное вывожу знак минус
            stosb
            fchs            ;и получаю модуль числа
    @@:     fstcw control   ;устанавливаю режим "округление к нулю"
            or control,0C00h
            fldcw control
            fld st          ;дублирую содержимое st(0) в st(1)
            frndint         ;округляю до целого содержимое st(0)
            fsub st(1),st   ;в st(1) остается дробная часть
            fldz
            fcomip st,st(1) ;сравниваю число с нулем
            jnz @f
            mov al,'0'
            stosb
            jmp b3
    @@:     call bcd2str
            fldz
    b3:     fcomip st,st(1)
            jz b0
            mov al,'.'
            stosb
            inc ebx
            fmul y          ;умножаю дробную часть на 1.0e17
            call bcd2str
    b0:     push 0
            push 0
            push offset String
            push 0
            call _imp__MessageBoxA@16
            retn
    bcd2str proc
            fbstp temp
            mov ecx,9       ;в десятом байте информация о знаке числа
            test ebx,ebx
            jnz b1
    @@:     cmp byte ptr [ecx-1+temp],0
            jnz b1
            loop @b         ;пропускаем незначащие (нулевые) разряды слева
    b1:     mov al,byte ptr [ecx-1+temp];загружаем первую значащую пару разрядов
            cmp al,9        ;если в старшей тетраде 0 - пропустить старшую тетраду
            ja @f
            add al,30h      ;младшую тетраду переводим в ASCII
            stosb
            dec ecx
            jz b2
    @@:     movzx ax,byte ptr [ecx-1+temp];распаковываем остальные разряды числа
            ror ax,4        ;выделяем старшую и младшую тетрады
            shr ah,4
            add ax,'00'     ;переводим в ASCII-код
            stosw
            loop @b
    b2:     retn
    bcd2str endp
    .data
    x dt -0.123456789;.987654321
    control dw ? ;переменная под содержимое регистра CWR
    temp dt ?    ;переменная под BCD
    y dq 1.0e17  ;множитель
    String db 30 dup (0)
    end start
    правда не работает с "нечислом" и бесконечностью
  3. или использовать crt_sprintf — например так
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    .386
    .model flat, stdcall
    option casemap:none
    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc
    include \masm32\include\msvcrt.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\msvcrt.lib
     
    .data
        format          db "%.4G",0
        szBuffer     db 1024 dup (?)
        r8Ticks     REAL8 2.777777777777777778E-7       ; 1/3600000
    .code
    start:invoke crt_sprintf,ADDR szBuffer,ADDR format,r8Ticks  ; the CRT only supports REAL8 here
        invoke MessageBox, NULL, addr szBuffer, 0, MB_OK
        invoke ExitProcess,NULL
    end start
  4. текст fptoa.asm автор Tim Roberts
    Кликните здесь для просмотра всего текста
    Assembler
    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
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    
    .486
        .model  flat
          option casemap :none  ; case sensitive
    ; Convert an 8-byte double-precision value to an ASCII string.  
    ;
    ; If the value is an integer of less than 16 digits, we convert it to
    ; an integral value.
    ;
    ; If the value is between 0.1 and 9999999.0, we convert it to a decimal
    ; value.
    ;
    ; Otherwise, we convert it to a scientific notation with 1 digit
    ; left and up to 6 digits right of the decimal, with a 3 digit exponent:
    ;    9.999999E+999
    ; Note that these rules differ somewhat from the '%g' specifier in printf.  
    ; But, since you have the source code, you can make this do whatever you 
    ; want.
    ; I've tried to include comments on how to convert this to use 10-byte
    ; doubles.
    ; These are bits in the FP status word.
     
    FP_LESSTHAN equ 01h
    FP_EQUALTO  equ 40h
     
        .data
    ; I'd rather this was local to the procedure, but masm doesn't do local arrays correctly.
    szTemp  db  18 dup (0)
        .code
    PowerOf10   proto
     
    ten dq  10.0
    ten7    dq  1.0e6
    ten17   dq  1.0e17
    rounder dq  5.0e10
     
     
    ; Convert a floating point register to ASCII.  For internal use.
    ; The result always has exactly 18 digits, with zero padding on the
    ; left if required.
    ;
    ; Entry:    ST(0) = a number to convert, 0 <= ST(0) < 1E19.
    ;       szTemp = an 18-character buffer.
    ;
    ; Exit:     szTemp = the converted result.
     
    FloatToBCD  PROC    public uses esi edi
     
        sub esp, 10
     
        ; The fbstp instruction converts the top of the stack to a
        ; packed BCD form in ten bytes, with two digits per byte.  The top 
        ; byte has the sign, which we ignore.
     
        fbstp [esp]
     
        ; Now we need to unpack the BCD to ASCII.
     
        lea esi, [esp+8]
        lea edi, [szTemp]
        mov ecx, 9
     
        .REPEAT
        mov al, [esi]          ; xxxx xxxx AAAA BBBB
        dec esi
        rol ax, 12             ; BBBB xxxx xxxx AAAA
        rol ah, 4              ; xxxx BBBB xxxx AAAA
        and ax, 0f0fh          ; 0000 BBBB 0000 AAAA
        add ax, 3030h          ; 3B3A
        mov [edi], ax
        add edi, 2
        dec ecx
        .UNTIL ZERO?
     
        add esp, 10
        ret
     
    FloatToBCD  ENDP
     
    ;
    ; Convert a double precision number to a string.
    ;
    ; Entry:    fpin = 8-byte double to convert
    ;       szDbl = character buffer
    ;
    ; Exit:     szDbl = converted value
    ;
    ; szDbl should be at least 19 bytes long.
    ;       
     
    FloatToStr  PROC    stdcall public USES esi edi, 
            fpin: QWORD, 
            szDbl: PTR CHAR
     
        LOCAL   iExp: DWORD
        LOCAL   stat: WORD
        LOCAL   mystat: WORD
     
    ; Special case zero.  fxtract fails for zero.
        
        mov edi, [szDbl]
     
        .if (dword ptr [fpin] == 0) && (dword ptr [fpin][4] == 0)
          mov byte ptr [edi], '0'
          mov byte ptr [edi][1], 0
          ret
        .endif
     
    ; Check for a negative number.
     
        .if (sdword ptr [fpin][4] < 0)
          and byte ptr [fpin][7], 07fh  ; change to positive
          mov byte ptr [edi], '-'       ; store a minus sign
          inc edi
        .endif
     
    ; Initialize the floating point unit and load our value onto the stack.
     
        fclex
        fstcw [stat]
        mov [mystat], 027fh
        fldcw [mystat]
     
        fld [fpin]
        fld st(0)
     
    ; Compute the closest power of 10 below the number.  We can't get an
    ; exact value because of rounding.  We could get close by adding in
    ; log10(mantissa), but it still wouldn't be exact.  Since we'll have to
    ; check the result anyway, it's silly to waste cycles worrying about
    ; the mantissa.
    ;
    ; The exponent is basically log2(fpin).  Those of you who remember
    ; algebra realize that log2(fpin) x log10(2) = log10(fpin), which is
    ; what we want.
     
        fxtract         ; ST=> mantissa, exponent, fpin
        fstp st(0)          ; drop the mantissa
        fldlg2          ; push log10(2)
        fmulp st(1), st     ; ST = log10(fpin), fpin
        fistp [iExp]        ; ST = fpin
     
    ; An 8-byte double can carry almost 16 digits of precision.  Actually, it's
    ; 15.9 digits, so some numbers close to 1E17 will be wrong in the bottom
    ; digit.  If this is a concern, change the '16' to a '15'.
    ;
    ; A 10-byte double can carry almost 19 digits, but fbstp only stores the
    ; guaranteed 18.  If you're doing 10-byte doubles, change the '16' to '18'.
     
        .IF ([iExp] < 16)
          fld st(0)         ; ST = fpin, fpin
          frndint           ; ST = int(fpin), fpin
          fcomp st(1)       ; ST = fpin, status set
          fstsw ax
          .IF (ah & FP_EQUALTO) ; if EQUAL
     
    ; We have an integer!  Lucky day.  Go convert it into a temp buffer.
     
        call FloatToBCD
     
        mov eax, 17
        mov ecx, [iExp]
        sub eax, ecx
        inc ecx
        lea esi, [szTemp][eax]
     
    ; The off-by-one order of magnitude problem below can hit us here.  
    ; We just trim off the possible leading zero.
     
        .IF (byte ptr [esi] == '0')
          inc esi
          dec ecx
        .ENDIF
     
    ; Copy the rest of the converted BCD value to our buffer.
     
        rep movsb
        jmp ftsExit
     
          .ENDIF
        .ENDIF
     
    ; We use the format [-]d.ddddddE+ddd.  That means we only need a maximum
    ; of 7 decimal places.  Let's have fbstp do our rounding for us.
     
        mov eax, 6
        sub eax, [iExp] ; adjust exponent to 7
        call PowerOf10
     
    ; Either we have exactly 7 digits, or we have exactly 6 digits.  We can
    ; detect that condition and adjust now.
     
        fcom [ten7]
        ; x0xxxx00 means top of stack > ten7
        ; x0xxxx01 means top of stack < ten7
        ; x1xxxx00 means top of stack = ten7
        fstsw ax
        .IF (ah & 1)
          fmul [ten]
          dec iExp
        .ENDIF
     
    ; Go convert to BCD.
     
        call FloatToBCD
     
        lea esi, [szTemp+11]    ; point to converted buffer
     
    ; If the exponent is between -1 and 6, we can express this as a number
    ; without scientific notation.
     
        mov ecx, iExp
        .IF (SDWORD PTR ecx >= -1) && (SDWORD PTR ecx <= 6)
     
    ; We need to copy ecx+1 digits, then a decimal point (maybe), then 
    ; the remaining 6-ecx digits.  If exponent is 0, add a leading 0.
     
          .IF (SDWORD PTR ecx == -1)
        mov byte ptr [edi], '0'
        inc edi
          .ENDIF
     
          inc ecx
          rep movsb
          mov byte ptr [edi], '.'
          inc edi
          mov ecx, 6
          sub ecx, [iExp]
          rep movsb
     
    ; Trim off trailing zeros.
     
          .WHILE (byte ptr [edi-1] == '0')
        dec edi
          .ENDW
     
    ; If we cleared out all the decimal digits, kill the decimal point, too.
     
          .IF (byte ptr [edi-1] == '.')
        dec edi
          .ENDIF
     
    ; That's it.
     
          jmp ftsExit
     
        .ENDIF
     
     
    ; Now convert this to a standard, usable format.  If needed, a minus
    ; sign is already present in the outgoing buffer, and edi already points
    ; past it.
     
        movsb               ; copy the first digit
        mov byte ptr [edi], '.'     ; plop in a decimal point
        inc edi
        movsd               ; copy four more digits
        movsw               ; copy two more digits
     
    if 0
     
    ; The printf %g specified trims off trailing zeros here.  I dislike
    ; this, so I've disabled it.  Comment out the if 0 and endif if you
    ; want this.
     
        .WHILE (byte ptr [edi][-1] == '0')
          dec edi
        .ENDW
    endif
    ; Shove in the exponent.  If you support 10-byte reals, remember to allow 4 digits for the exponent.
        mov byte ptr [edi], 'e' ; start the exponent
        mov eax, [iExp]
        .IF (sdword ptr eax < 0)    ; plop in the exponent sign
          mov byte ptr [edi][1], '-'
          neg eax
        .ELSE
          mov byte ptr [edi][1], '+'
        .ENDIF
     
        mov ecx, 10
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][4], dl    ; shove in the ones exponent digit
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][3], dl    ; shove in the tens exponent digit
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][2], dl    ; shove in the hundreds exponent digit
     
        add edi, 5      ; point to terminator
     
    ; Clean up and go home.
     
    ftsExit:
        mov byte ptr [edi], 0
        fldcw [stat]        ; restore control word
        fwait
     
        ret
     
    FloatToStr  ENDP
        end
  5. текст fptoa2.asm автор Tim Roberts
    Кликните здесь для просмотра всего текста
    Assembler
    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
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    
    .486
        .model  flat
          option casemap :none  ; case sensitive
     
     
    ; Convert an 8-byte double-precision value to an ASCII string.  
    ;
    ; If the value is an integer of less than 16 digits, we convert it to
    ; an integral value.
    ;
    ; If the value is between 0.1 and 9999999.0, we convert it to a decimal
    ; value.
    ;
    ; Otherwise, we convert it to a scientific notation with 1 digit
    ; left and up to 6 digits right of the decimal, with a 3 digit exponent:
    ;    9.999999E+999
    ;
    ; Note that these rules differ somewhat from the '%g' specifier in printf.  
    ; But, since you have the source code, you can make this do whatever you 
    ; want.
    ;
    ; I've tried to include comments on how to convert this to use 10-byte
    ; doubles.
    ; These are bits in the FP status word.
    FP_LESSTHAN equ 01h
    FP_EQUALTO  equ 40h
     
        .data
     
    ; I'd rather this was local to the procedure, but masm doesn't do 
    ; local arrays correctly.
     
    szTemp  db  18 dup (0)
     
        .code
     
    PowerOf10   proto
     
    ten dq  10.0
    ten16   dq  1.0e16
    rounder dq  5.0e10
     
     
    ; Convert a floating point register to ASCII.  For internal use.
    ; The result always has exactly 18 digits, with zero padding on the
    ; left if required.
    ;
    ; Entry:    ST(0) = a number to convert, 0 <= ST(0) < 1E19.
    ;       szTemp = an 18-character buffer.
    ;
    ; Exit:     szTemp = the converted result.
     
    FloatToBCD2 PROC    public uses esi edi
     
        sub esp, 10
     
        ; The fbstp instruction converts the top of the stack to a
        ; packed BCD form in ten bytes, with two digits per byte.  The top 
        ; byte has the sign, which we ignore.
     
        fbstp [esp]
     
        ; Now we need to unpack the BCD to ASCII.
     
        lea esi, [esp+8]
        lea edi, [szTemp]
        mov ecx, 9
     
        .REPEAT
        mov al, [esi]       ; xxxx xxxx AAAA BBBB
        dec esi
        rol ax, 12      ; BBBB xxxx xxxx AAAA
        rol ah, 4       ; xxxx BBBB xxxx AAAA
        and ax, 0f0fh       ; 0000 BBBB 0000 AAAA
        add ax, 3030h       ; 3B3A
        mov [edi], ax
        add edi, 2
        dec ecx
        .UNTIL ZERO?
     
        add esp, 10
        ret
     
    FloatToBCD2 ENDP
     
    ;
    ; Convert a double precision number to a string.
    ;
    ; Entry:    fpin = 8-byte double to convert
    ;       szDbl = character buffer
    ;
    ; Exit:     szDbl = converted value
    ;
    ; szDbl should be at least 19 bytes long.
    ;       
     
    FloatToStr2 PROC    stdcall public USES esi edi, 
            fpin: QWORD, 
            szDbl: PTR CHAR
     
        LOCAL   iExp: DWORD
        LOCAL   stat: WORD
        LOCAL   mystat: WORD
     
    ; Special case zero.  fxtract fails for zero.
        
        mov edi, [szDbl]
     
        .if (dword ptr [fpin] == 0) && (dword ptr [fpin][4] == 0)
          mov byte ptr [edi], '0'
          mov byte ptr [edi][1], 0
          ret
        .endif
     
    ; Check for a negative number.
     
        .if (sdword ptr [fpin][4] < 0)
          and byte ptr [fpin][7], 07fh  ; change to positive
          mov byte ptr [edi], '-'       ; store a minus sign
          inc edi
        .endif
     
    ; Initialize the floating point unit and load our value onto the stack.
     
        fclex
        fstcw [stat]
        mov [mystat], 027fh
        fldcw [mystat]
     
        fld [fpin]
        fld st(0)
     
    ; Compute the closest power of 10 below the number.  We can't get an
    ; exact value because of rounding.  We could get close by adding in
    ; log10(mantissa), but it still wouldn't be exact.  Since we'll have to
    ; check the result anyway, it's silly to waste cycles worrying about
    ; the mantissa.
    ;
    ; The exponent is basically log2(fpin).  Those of you who remember
    ; algebra realize that log2(fpin) x log10(2) = log10(fpin), which is
    ; what we want.
     
        fxtract         ; ST=> mantissa, exponent, fpin
        fstp st(0)          ; drop the mantissa
        fldlg2          ; push log10(2)
        fmulp st(1), st     ; ST = log10(fpin), fpin
        fistp [iExp]        ; ST = fpin
     
    ; An 8-byte double can carry almost 16 digits of precision.  Actually, it's
    ; 15.9 digits, so some numbers close to 1E17 will be wrong in the bottom
    ; digit.  If this is a concern, change the '16' to a '15'.
    ;
    ; A 10-byte double can carry almost 19 digits, but fbstp only stores the
    ; guaranteed 18.  If you're doing 10-byte doubles, change the '16' to '18'.
     
        .IF ([iExp] < 16)
          fld st(0)         ; ST = fpin, fpin
          frndint           ; ST = int(fpin), fpin
          fcomp st(1)       ; ST = fpin, status set
          fstsw ax
          .IF (ah & FP_EQUALTO) ; if EQUAL
     
    ; We have an integer!  Lucky day.  Go convert it into a temp buffer.
     
        call FloatToBCD2
     
        mov eax, 17
        mov ecx, [iExp]
        sub eax, ecx
        inc ecx
        lea esi, [szTemp][eax]
     
    ; The off-by-one order of magnitude problem below can hit us here.  
    ; We just trim off the possible leading zero.
     
        .IF (byte ptr [esi] == '0')
          inc esi
          dec ecx
        .ENDIF
     
    ; Copy the rest of the converted BCD value to our buffer.
     
        rep movsb
        jmp ftsExit
     
          .ENDIF
        .ENDIF
     
    ; Have fbstp round to 17 places.
     
        mov eax, 16; experiment
        sub eax, [iExp] ; adjust exponent to 17
        call PowerOf10
     
    ; Either we have exactly 17 digits, or we have exactly 16 digits. We can detect that condition and adjust now.
     
        fcom [ten16]
        ; x0xxxx00 means top of stack > ten16
        ; x0xxxx01 means top of stack < ten16
        ; x1xxxx00 means top of stack = ten16
        fstsw ax
        .IF (ah & 1)
          fmul [ten]
          dec iExp
        .ENDIF
     
    ; Go convert to BCD.
     
        call FloatToBCD2
     
        lea esi, [szTemp+1]     ; point to converted buffer
     
    ; If the exponent is between -15 and 16, we should express this as a number
    ; without scientific notation.
     
        mov ecx, iExp
        .IF (SDWORD PTR ecx >= -15) && (SDWORD PTR ecx <= 16)
     
    ; If the exponent is less than zero, we insert '0.', then -ecx
    ; leading zeros, then 16 digits of mantissa.  If the exponent is
    ; positive, we copy ecx+1 digits, then a decimal point (maybe), then the remaining 16-ecx digits.
     
          inc ecx
          .IF (SDWORD PTR ecx <= 0)
            mov word ptr [edi], '.0'
        add edi, 2
        neg ecx
        mov al, '0'
        rep stosb
        mov ecx, 16
          .ELSE
            rep movsb
            mov byte ptr [edi], '.'
            inc edi
            mov ecx, 16
            sub ecx, [iExp]
          .ENDIF
          rep movsb
     
    ; Trim off trailing zeros.
     
          .WHILE (byte ptr [edi-1] == '0')
        dec edi
          .ENDW
     
    ; If we cleared out all the decimal digits, kill the decimal point, too.
     
          .IF (byte ptr [edi-1] == '.')
        dec edi
          .ENDIF
     
    ; That's it.
     
          jmp ftsExit
     
        .ENDIF
    ; Now convert this to a standard, usable format.  If needed, a minus sign is already present in the outgoing 
    ;buffer, and edi already points past it.
        movsb               ; copy the first digit
        mov byte ptr [edi], '.'     ; plop in a decimal point
        inc edi
        movsd               ; copy four more digits
        movsw               ; copy two more digits
     
    if 0
     
    ; The printf %g specified trims off trailing zeros here.  I dislike
    ; this, so I've disabled it.  Comment out the if 0 and endif if you
    ; want this.
     
        .WHILE (byte ptr [edi][-1] == '0')
          dec edi
        .ENDW
    endif
     
    ; Shove in the exponent.  If you support 10-byte reals, remember to
    ; allow 4 digits for the exponent.
     
        mov byte ptr [edi], 'e' ; start the exponent
        mov eax, [iExp]
        .IF (sdword ptr eax < 0)    ; plop in the exponent sign
          mov byte ptr [edi][1], '-'
          neg eax
        .ELSE
          mov byte ptr [edi][1], '+'
        .ENDIF
     
        mov ecx, 10
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][4], dl    ; shove in the ones exponent digit
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][3], dl    ; shove in the tens exponent digit
     
        xor edx, edx
        div ecx
        add dl, '0'
        mov [edi][2], dl    ; shove in the hundreds exponent digit
     
        add edi, 5      ; point to terminator
     
    ; Clean up and go home.
     
    ftsExit:
        mov byte ptr [edi], 0
        fldcw [stat]        ; restore control word
        fwait
     
        ret
     
    FloatToStr2 ENDP
     
        end
  6. код Mr_Ser_Win, взято здесь
    функция, которая преобразовывает строку в число. Общий принцип процедуры такой: начала загружаем в st0 ноль в цикле, смотрим очередной символ в строке, преобразовываем этот символ в число и прибавляем к st0, предварительно умножив его на 10. После всего проделанного смотрим, сколько у нас символов после запятой и делим st0 на 10 столько раз, сколько у нас символов после запятой.
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    STR_to_FLOAT:
    ; converting string to float value
    ;IN
    ;   ESI – указатель на строку
    ;   EDI – указатель на переменную, в которую надо сохранить значение
     
        decimal_separator equ '.'
     
        pushad
        finit  
     
        xor ebp, ebp
        cmp byte [esi], '-'
        jnz @f
        inc esi
        inc ebp
       @@:
        xchg edi, esi
        call GetZSLength ;получаем длину строки
        xchg edi, esi
     
        mov ecx, eax
        mov edx, eax
     
        mov ebx, 10
     
        fldz
        xor eax, eax
        push eax  ; [esp] - temp value
     
       .repeat:
        cmp byte [esi], '9'
        ja .error
        cmp byte [esi], '0'
        jnb @f
        cmp byte [esi], decimal_separator
        jnz .error
        jmp .continue
       @@:
        mov [esp], ebx
        fimul dword [esp]
     
        mov al, byte [esi]
        sub al, '0'
        mov [esp], eax
        fiadd dword [esp]
     
       .continue:
        dec ecx
        inc esi
        cmp ecx, 0
        jnz .repeat
       .endrepeat:
     
        xchg esi, edi
        mov al, decimal_separator
        mov ecx, edx
        sub edi, edx
        repnz scasb  ; в ecx количество символов после запятой
     
        cmp ecx, 0
        jz .end
        mov dword [esp], 10
       .rep:
        fidiv dword [esp]
        loop .rep
       .end:
     
        xor eax, eax
        cmp ebp, eax
        jz @f
        fld1
        fld1
        fld1
        faddp st1, st0 ; st0 = 2, st1 = 1
        fsubp st1, st0 ; st0 = -1
        fmulp st1, st0
       @@:
        fstp qword [esi]  ; <------ saving float value
       .error:
        pop eax ; delete temp value
        popad
        ret
    GetZSLength:
    ; get zero-string length
    ;IN
    ;       EDI ZS offset
    ;OUT
    ;       EAX ZS length
     
        push ecx
        push esi
        push edi
     
        cld
        xor   al, al
        mov ecx, 0FFFFFFFFh
        mov esi, edi
        repne scasb
        sub edi, esi
        mov eax, edi
        dec eax
     
        pop edi
        pop esi
        pop ecx
        ret
  7. Фрагмент из книги "Программирование на ассемблере" Дэвид Дж. Брэдли
    Подпрограмма берет число из вершины стека, преобразует его в печатную строку символов и посылает ее на экран.
    Если исходное число NAN, либо бесконечность, или другое специальное число сопроцессора, результат будет показан неверно. В первой части программы можно использовать команду FXAM, которая определила бы тип числа на вершине стека. Но в данном примере эта команда не используется, так как предполагается, что исходное число имеет нужный тип.
    Эта программа не приспособлена для оформления формата выводимого числа. Результат всегда содержит знак (либо пробел, либо знак "-") и целую часть, состоящую из одной цифры. После десятичной точки расположены восемь десятичных позиций, а затем буква "E" и три позиции цифр для степени 10. Результат работы этой программы не так хорош, как можно было желать, но он позволяет видеть результат работы программы в читабельной форме. Более красивое преобразование числа потребовало бы значительно больше команд, и лишь малая часть из них помогла бы пониманию работы сопроцессора.
    Программа преобразования работает следующим образом. Сначала она определяет порядок числа. Например, число 1234 имеет порядок 3; это означает, что оно находится между значениями 103 и 104. Найдя правильный порядок числа, программа сохраняет его значение (показатель степени результата) и делит исходное число на 10 в этой степени. Теперь число находится в интервале от 1 до 10. Затем программа умножает число на 108. Запись этого числа в десятичной форме дает девять десятичных цифр; старшая цифра - целая часть, младшие восемь цифр - дробные разряды.
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    .data
    old_cw          dw      ?
    new_cw          dw      ?
    exponent        dw      ?
    bcd_result      dt      ?
    bcd_exponent    dt      ?
    ten8            dd      100000000
    print_string    db      '         E    ',10,13,'$'
    ;--------------------------------------------
    ; Эта программа извлекает из вершины стека сопроцессора число и выводит его на экран в плавающем формате.
    ; Параметры: число в ST(0)
    ; Результат: изображение числа на экране;
    ;--------------------------------------------
    float_ASCII     proc      ;----ST(0)---;----ST(1)--;--ST(2)--
                              ; X          ; ?         ; ?
        fld st(0)         ; X          ; X         ; ?
            fabs              ;|X|         ; X         ; ?
            fld1              ; 1          ;|X|        ; X
        fxch st(1)        ; |X|        ; 1         ; X
            fyl2x             ; LOG2(X)    ; X         ; ?
            fldl2t            ; LOG2(10)   ; LOG2(X)   ; X
            fdivrp st(1),st(0);E=LOGX/LOG10; X         ; ?
            fnstcw old_cw                           
            fwait                                   
            mov ax,old_cw                           
            and ax,not 0C00h
            or ax,400h                              
            mov new_cw,ax                           
            fldcw new_cw                            
            frndint           ;I = INT(E) ; X         
            fldcw old_cw                            
            fist exponent     ; I         ; X          
            fchs              ; -I        ; X          
            call ten_to_x     ; 10^(-I)   ; X          
            fmulp st(1),st(0) ; X/10^I               
            fimul ten8        ; Мантисса             
            fbstp bcd_result  
            fild exponent     ; I                    
            fbstp bcd_exponent
    ;Вывод на экран значений,запомненных как упакованные целые двоично-десятичные числа
            lea     di,print_string ; Указатель на выводимую строку
            mov     al,byte ptr bcd_result+9
            call    PRINT_SIGN                  ; Печать знака
            mov     al,byte ptr bcd_result+4
            call    PRINT_NYBBLE                ; Печать первой цифры
            mov     al,'.'                      ; Десятичная точка
            stosb
            lea     bx,bcd_result+3
            mov     cx,4   ; Печать 8 байт (16 цифр) после десятичной точки
    do_byte:call    PRINT_BYTE
            loop    do_byte
            mov     al,'E'                          ; Символ экспоненты
            stosb
        mov     al,byte ptr bcd_exponent+9
            call    PRINT_SIGN                  ; Печать знака экспоненты
            mov     al,byte ptr bcd_exponent+1
            call    PRINT_NYBBLE                ; Первая цифра экспоненты
            lea     bx,bcd_exponent
            call    PRINT_BYTE                  ; Оставшиеся цифры
            lea     dx,print_string
            mov     ah,9
            int     21h                         ; Вывод всей строки на экран
            ret
    float_ASCII     endp
    ;-----  Эта подпрограмма выводит ' ' или '+'
    PRINT_SIGN      proc    
            cmp     al,0                 ; Проверка на знак
            mov     al,' '                 ; Занесение положительного знака
            jz      positive
            mov     al,'-'                 ; Занесение минуса
    positive:stosb                         ; Сохранение в выводимой строке
            ret
    PRINT_SIGN      endp
    ;Эта процедура печатает две десятичные цифры, находящиеся в памяти по адресу [bx]
    PRINT_BYTE      proc    
            mov     al,[bx]              ; Выборка байта
            shr     al,4                ; Сдвиг старшей цифры
            call    PRINT_NYBBLE          ; Печать старшей цифры
            mov     al,[bx]              ; Выборка младшей цифры
            call    PRINT_NYBBLE          ; Печать младшей цифры
            dec     bx              ; Переход на следующую пару цифр
            ret
    PRINT_BYTE      endp
    ;-----  Печать одной десятичной цифры из регистра al
    PRINT_NYBBLE    proc    
            and     al,0Fh               ; Выделение младшей цифры
            add     al,'0'                 ; Преобразование в символ
            stosb                         ; Сохранение в выводимой строке
            ret
    PRINT_NYBBLE    endp
    ;--------------------------------------------
    ; Эта программа извлекает число с вершины стека сопроцессора и возводит 10 в эту степень.
    ; Параметры: X в ST(0)
    ; Результат: 10^X в ST(0)
    ; Эта программа использует две ячейки в стеке FPU плюс параметр - всего три ячейки.
    ;--------------------------------------------
    ten_to_x          PROC   
                              ;----ST(0)------;-----ST(1)-----;--ST(2)--
                              ; X             ; ?             ; ?
        fldl2t            ; LOG2(10)      ; X             ; ?
            fmulp st(1),st(0) ; X*LOG2(10)=E  ; ?             ; ?
            fnstcw old_cw     ;---------------;---------------;--------
            fwait             ; Выборка текущего слова состояния
            mov ax,old_cw     ; Сохранение слова сотояния
            and ax,not 0C00h  ; Установка способа округления к минус
            or ax,400h       ;      бесконечности
            mov new_cw,ax
            fldcw new_cw      ;---------------;---------------;--------
            fld1              ; 1             ; E             ; ?
            fchs              ; -1            ; E             ; ?
            fld st(1)         ; E             ; -1            ; E
            frndint           ; INT(E) = I    ; -1            ; E
            fldcw old_cw      ;               ;               ;
            fxch st(2)        ; E             ; -1            ; I
            FSUB st(0),st(2)  ; E - I = F     ; -1            ; I
            FSCALE            ; F*2^(-1) = F/2; -1            ; I
            F2XM1             ; (2^F/2)-1     ; -1            ; I
            FSUBRP st(1),st(0); 2^F/2         ; I             ; ?
            FMUL st(0)        ; 2^F           ; I             ; ?
            FSCALE            ; (2^F)*(2^I)   ; I             ; ?
            fxch st(1)        ; I             ; 2^(I+F)       ; ?
            FCOMP             ; 2^(I+F)       
            ret               ; 10^X         
    ten_to_x          ENDP
    END
    Первая часть программы определяет правильный порядок исходного числа. В программе логарифм числа по основанию 10 находится с помощью формулы:
    https://www.cyberforum.ru/cgi-bin/latex.cgi?lg(X) = \frac{log_{2}(X)}{log_{2}(10)}
    Затем округляется порядок в направлении минус бесконечности, опять используя управление округлением. Мы используем константу TENB (которая содержит целое число 108) для того, чтобы вернуть число в нужный диапазон. Команда FBSTP дважды преобразует числа в десятичное представление; сначала она дает нам девять цифр мантиссы числа, а затем - три цифры порядка.
    Остальная часть программы выполняет символьную обработку, необходимую для преобразования десятичного представления в строки символов. Программа определяет и показывает знаки числа и порядка. Она распаковывает десятичные байты и преобразует их в символы; подпрограмма PRINT_BYTE делает распаковку, а подпрограмма PRINT_NYBBLE выполняет преобразование в символы. Все символы имеют значения от "0" до "9". (Но если исходное число - одно из неопределенных чисел, символьная строка будет содержать "непонятные" символы).
    Эта программа верно печатает любое число, лежащее в диапазоне длинных действительных чисел. Любое число, выходящее за пределы возможностей этого представления (например, 101234) имеет поле порядка, сокращенное до трех цифр. Конечно, вы можете изменить программу так, чтобы она обрабатывала четыре цифры поля порядка, если вы этого желаете. Но существует все же число, которое программой обрабатывается верно, но вы, возможно, пожелает изменить его изображение. Если исходное число 0, результат печатается в виде "0.00000000E-932". Так происходит потому, что поле порядка имеет смещение; сопроцессор представляет число 0 с минимально возможным порядком (-4932) и с нулевой мантиссой. Когда программа преобразует число в код ASCII, она верно печатает мантиссу и порядок (за исключением того, что ей приходится усекать порядок до трех цифр). Если вы захотите обрабатывать такой порядок отдельно, то измените программу, вставив в нее проверку на нуль (чаще всего, с помощью команды FTST) в самом начале, рассматривая это, как специальный случай.
преобразование строки в число
  1. код alexcoder, взято здесь
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    value_s2f       dw      0                       ; переменная для функции преобразования строки в число 
    ;| SI - указатель на строку                     |
    ;+----------------------------------------------+
    ;| ST(0) - преобрезованное число                |
    ;| cf=1 если в строке ошибка                    |
    STRTOFLOAT      PROC
            jmp             @STARTCONVERSATION_S2F  ; стартуем в рабочую область
    @STARTCONVERSATION_S2F:
            pusha
            mov             value_s2f, 0                    ; обнуляем локальную переменную
            xor             bx, bx                          ; очищаем указатель позиции
            cmp     byte ptr [si], '-'              ; проверяем на отрицательность
            jne             @POSITIVE_S2F           ; число не отрицательное
            inc             bx                                      ; число отрицательное, увеличиваем позицию
    @POSITIVE_S2F:
            mov             value_s2f, 10           ; загружаем число в переменную
            fild            value_s2f                       ; загружаем в стэк число 10
            fldz                                                    ; загружаем в стэк число 0
    @REPEAT_BEFORE:
            mov             al, byte ptr si[bx]             ; получаем символ
            cmp     al, byte ptr '.'                        ; проверяем точка ли это
            je              @ISPOINTBEFORE  ; точка - идем дальше
            cmp     al, byte ptr 13                 ; проверяем конец ли строкиа
            je              @ENDASINT               ; уже конец и хватит искать дроби
            cmp     al, '0'                         ; если текущий символ не цифра
            jc      @END_S2F_ERR                    ; то выход с ошибкой
            cmp     al,'9'
            ja      @END_S2F_ERR
            sub             al, 30h                         ; делаем из CHAR - INT
            mov             byte ptr value_s2f, al  ; копируем в память
            fiadd   value_s2f                       ; складываем из тем, что есть в стэке
            fmul    st(0), st(1)                    ; умножаем на 10
            inc             bx                                      ; увеличиваем указатель
            jmp             @REPEAT_BEFORE  ; повторяем
    @ISPOINTBEFORE:
            inc             bx                                      ; увеличиваем указатель
            fdiv            st(0), st(1)                    ; делим число на 10, т.к. оно уже больше
            fxch            st(1)                           ; меняем местами регистры
            mov             al, byte ptr 13                 ; ищем символ конца строки
    @FINDNEXT:
            cmp     si[bx], al                              ; ищем конец строки
            je              @FINDEND                        ; нешел конец строки
            inc             bx                                      ; получаем следующий адрес символа
            jmp             @FINDNEXT                       ; не нашел, еще ищем
    @FINDEND: dec bx                                      ; переходим на предыдущий символ
            fldz                                                    ; загружаем в стэк число 0
    @REPEAT_AFTER:
            mov             ax, word ptr si[bx]             ; получаем символ
            cmp     al, byte ptr '.'                        ; проверяем точка ли это
            je              @WASPOINTAFTER  ; точка - идем дальше
            cmp     al, '0'                 ; если текущий символ не цифра
            jc      @END_S2F_ERR            ; то выход с ошибкой
            cmp     al,'9'
            ja      @END_S2F_ERR
            sub             al, 30h                         ; делаем из CHAR - INT
            mov             byte ptr value_s2f, al  ; копируем в память
            fiadd   value_s2f                       ; складываем из тем, что есть в стэке
            fdiv            st(0), st(1)                    ; делим на 10
            dec             bx                                      ; декрементируем указатель
            loop    @REPEAT_AFTER   ; повторяем
    @WASPOINTAFTER:
            fxch            st(1)                           ; меняем число 10 и остаток местами
            fxch            st(2)                           ; меняем целое и 10 местами
            faddp   st(1)                           ; складываем число до и после запятой
            fxch            st(1)                           ; меняем местами результат и 10
            fistp           value_s2f                       ; извлекаем из стэка 10
            jmp             @FULLEND                        ; полный конец процедуры
    @ENDASINT:
            fdiv            st(0), st(1)                    ; делим число на 10, т.к. оно уже больше
            fxch            st(1)                           ; меняем местами регистры
            fistp           value_s2f                       ; извлекаем из стэка 10
    @FULLEND:
            cmp     byte ptr [si], '-'              ; проверяем на отрицательность
            jne             @END_S2F                        ; число не отрицательное
            fchs                                                    ; число отрицательное, меняем знак
    @END_S2F:
            popa                                            ; выгружаем все регистры
            clc                                             ;ошибки нет
            ret                                                     ; возврат из процедуры
     
    @END_S2F_ERR:
            popa                                            ; выгружаем все регистры
            fistp value_s2f                         ;очищаем st0
            stc  ;ошибка
            ret ; возврат из процедуры
    STRTOFLOAT      ENDP
  2. текст atofp.asm автор Tim Roberts
    Кликните здесь для просмотра всего текста
    Assembler
    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
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    
    .486
        .model  flat
          option casemap :none  ; case sensitive
    ; Convert a string containing an ASCII representation of a floating point value 
    ;to an 8-byte double precision number.  Returns a pointer to the first character 
    ;it couldn't convert.
    ; char * StrToFloat( char * szIn, double * fpOut )
    ; Here is a perl regular expression to describe the formats we accept:
    ;    (+|-)?[0-9]+(.[0-9]*)?(E(+|-)[0-9]+)
    .code
    ten dq  10.0
    ten_1   dt  1.0e1
        dt  1.0e2
        dt  1.0e3
        dt  1.0e4
        dt  1.0e5
        dt  1.0e6
        dt  1.0e7
        dt  1.0e8
        dt  1.0e9
        dt  1.0e10
        dt  1.0e11
        dt  1.0e12
        dt  1.0e13
        dt  1.0e14
        dt  1.0e15
    ten_16  dt  1.0e16
        dt  1.0e32
        dt  1.0e48
        dt  1.0e64
        dt  1.0e80
        dt  1.0e96
        dt  1.0e112
        dt  1.0e128
        dt  1.0e144
        dt  1.0e160
        dt  1.0e176
        dt  1.0e192
        dt  1.0e208
        dt  1.0e224
        dt  1.0e240
    ten_256 dt  1.0e256
    ; The remaining exponents are only necessary if we decide to support
    ; 10-byte doubles.  FloatToStr and StrToFloat only support 8-byte,
    ; but PowerOf10 doesn't care, so we'll include them.
        dt  1.0e512
        dt  1.0e768
        dt  1.0e1024
        dt  1.0e1280
        dt  1.0e1536
        dt  1.0e1792
        dt  1.0e2048
        dt  1.0e2304
        dt  1.0e2560
        dt  1.0e2816
        dt  1.0e3072
        dt  1.0e3328
        dt  1.0e3584
        dt  1.0e4096
        dt  1.0e4352
        dt  1.0e4608
        dt  1.0e4864
    ; Multiply a floating point value by an integral power of 10.
    ; Entry: EAX = power of 10, -4932..4932.
    ;   ST(0) = value to be multiplied
    ; Exit: ST(0) = value x 10^eax
    PowerOf10   PROC    public
        mov ecx, eax
        .IF (SDWORD PTR eax < 0)
        neg eax
        .ENDIF
        fld1
        mov dl, al
        and edx, 0fh
        .IF (!ZERO?)
        lea edx, [edx+edx*4]
        fld ten_1[edx*2][-10]
        fmulp st(1), st
        .ENDIF
     
        mov dl, al
        shr dl, 4
        and edx, 0fh
        .IF (!ZERO?)
        lea edx, [edx+edx*4]
        fld ten_16[edx*2][-10]
        fmulp st(1), st
        .ENDIF
     
        mov dl, ah
        and edx, 1fh
        .IF (!ZERO?)
        lea edx, [edx+edx*4]
        fld ten_256[edx*2][-10]
        fmulp st(1), st
        .ENDIF
     
        .IF (SDWORD PTR ecx < 0)
        fdivp st(1), st
        .ELSE
        fmulp st(1), st
        .ENDIF
        ret
    PowerOf10   ENDP
    ; Convert a string to a double-precision floating point number
    ; char * StrToFloat( char * str, double * fpout );
    StrToFloat  PROC    stdcall public, szIn: PTR BYTE, fpout: PTR QWORD
        LOCAL   sign: BYTE
        LOCAL   expsign: BYTE
        LOCAL   decimal: DWORD
        LOCAL   stat: WORD
        LOCAL   temp: WORD
        xor eax, eax
        mov [sign], al
        mov [expsign], al
        mov [decimal], -1
        fstcw [stat]
        mov [temp], 027fh
        fldcw [temp]
    ; First, see if we have a sign at the front end.
        mov esi, [szIn]
        mov al, [esi]
        .IF (al == '+')
        inc esi
        mov al, [esi]
        .ELSEIF (al == '-')
        inc esi
        mov [sign], 1
        mov al, [esi]
        .ENDIF
     
        cmp al, 0       ; null string?
        je sdExit
     
    ; Initialize the floating point unit.
        fclex
        xor ebx, ebx
        fldz
        xor ecx, ecx
    ; OK, now start our main loop.
    ;   esi => character in string now in al
    ;   al = next character to be converted
    ;   ebx = number of digits encountered thus far
    ;   ecx = exponent
    ;   ST(0) = accumulator
     
    cvtloop:
        cmp al, 'E'
        je doExponent
        cmp al, 'e'
        je doExponent
     
        .IF (al == '.')
        mov [decimal], ebx  ; remember decimal point location
        .ELSE
        sub al, '0'     ; convert ASCII to BCD
        jb sdFinish ; if not a digit
        cmp al, 9
        ja sdFinish ; if not a digit
        mov [temp], ax
        fmul [ten]      ; d *= 10
        fiadd [temp]        ; d += new digit
        inc ebx     ; increment digit counter
        .ENDIF
        inc esi
        mov al, [esi]
        jnz cvtloop
        jmp sdFinish
     
    ;We have the mantissa at the top of the stack. Now convert the exponent. Fortunately, this is an integer.
    ;   esi = pointer to character in al
    ;   al = next character to convert
    ;   ebx = digit counter
    ;   ecx = accumulated exponent
    ;   ST(0) = mantissa
    doExponent:
        inc esi
        mov al, [esi]
        cmp al, 0
        jz sdFinish
        ; Does the exponent have a sign?
        .IF (al == '+')
        inc esi
        mov al, [esi]
        .ELSEIF (al == '-')
        inc esi
        mov [expsign], 1
        mov al, [esi]
        .ENDIF
        cmp al, 0
        jz sdFinish
    expLoop: sub al, '0'
        jb sdFinish
        cmp al, 9
        ja sdFinish
        imul ecx, 10
        add ecx, eax
        inc esi
        mov al, [esi]
        jnz expLoop
    ;Adjust the exponent to account for decimal places. At this juncture, we work with the absolute value of the exponent.  
    ;That means we need to subtract the adjustment if the exponent will be negative, add if the exponent will be positive.
    ;  ST(0) = mantissa
    ;  ecx = unadjusted exponent
    ;  ebx = total number of digits
    sdFinish:
        .IF ([expsign] != 0)
        neg ecx
        .ENDIF
        mov eax, [decimal]
        .IF (eax != -1)
        sub ebx, eax    ; ebx = digits to right of decimal place
        sub ecx, ebx    ; adjust exponent
        .ENDIF
    ; Multiply by 10^exponent.
    ;  ST(0) = mantissa
    ;  ecx = exponent
        mov eax, ecx
        call PowerOf10
    ; Negate the whole thing, if necessary.
        .IF ([sign] != 0)
        fchs
        .ENDIF
    ; That's it!  Store it and go home.
        mov edi, [fpout]
        fstp qword ptr [edi]    ; store the reslt
        fwait
    sdExit:    fldcw [stat]
        mov eax, esi    ; return pt to next unread char
        ret
    StrToFloat  ENDP
        end
  3. код автор Mr_Ser_Win, взято здесь
    Теперь напишем процедуру преобразования числа в строку. Общий принцип её работы такой: сначала нормализуем число, т.е. делаем его меньше единицы, в цикле деля его на 10 пока она не станет меньше нуля, заодно мерим, сколько у нас символов ДО запятой. После чего в цикле умножаем число на 10 и сохраняем его как целое число, отбросив дробную часть, после чего полученное число преобразовываем с символ, прибавив к нему 30h. Кстати, надо не забыть вычесть из st0 полученную целую часть. Делаем эту операцию столько раз, сколько у нас символов ДО запятой. После чего добавляем к строке символ разделитель. Дальше производим аналогичную операцию, но только столько раз чему у нас равна точность результата (количество символов после запятой).
    Кликните здесь для просмотра всего текста
    Assembler
    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
    
    FLOAT_to_STR:
    ;converting float value to ZS
    ;IN
    ;       EAX – указатель на переменную
    ;       EDX – точность, количество символов после запятой
    ;       ESI – указатель на буфер со строкой
        plus_one  equ 0031h; '1',0
        zero equ 0030h; '0',0
     
        pushad
        finit
        fld qword [eax]  ;<--- st0 = float value
        fldz
        fcomip st1       ;
        jz .zero         ; if value = 0
        jb @f
        xor ebx, ebx
        dec ebx
        push ebx
        fild dword [esp]
        pop ebx
        fxch  ; xchg st0, st1
          ; st0 = float value
        fmul st0, st1      ; st0 = -st0
        mov byte [esi], '-'
        inc esi
    @@: fld1  ; st0 = 1
        fcomip st1
        jz .one
        jb .normalize
        jmp .translate
    .normalize:    xor ecx, ecx
        mov eax, 0.1
        push eax
        fld1
        fxch
    .rep1: fmul dword [esp]
        inc ecx
        fcomi st1
        jb @f
        jmp .rep1
    @@: pop eax
    .translate: xchg edx, ecx; edx = digit count before spot
        add ecx, edx ; ecx = digit count before + after spot
        mov eax, 10
        push eax
        fild dword [esp] ; st0 = 10
                 ; in dword [esp] temp value  !!!!!!
        fxch         ; st0 = float value
                 ; st1 = 10
    .rep2: fmul st0, st1
        fld1
        fcmovne st0, st1
        fisttp dword [esp]; trunc st0 and pop it to dword [esp]
        mov al, byte [esp];
        fild dword [esp]  ; st0 = current truncuated digit
        fsubp st1, st0
        add al, 30h ; al from number to char
        mov byte [esi], al
        inc esi
        dec edx
        cmp edx,0
        jnz @f
        mov byte [esi], '.'
        inc esi
    @@: loop .rep2
        pop eax
        jmp .end
    .zero: mov word [esi], zero
        jmp .end
    .one: mov word [esi],plus_one
        jmp .end
    .error:
    .end: popad
        ret
0
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
05.01.2017, 19:29 5
Вывод чисел в DOS

Добавлю и "свои" способы ввода и вывода.

Они ничем не лучше, но и не хуже приведённых выше. Причина публикации в том, что новички, заглядывая в данную тему, не могут понять, как пользоваться приведёнными фрагментами кода.

Вывод целого беззнакового 16-разрядного числа (DOS)

Подпрограмма в основном повторяет аналогичный код Goodwin98, отличие лишь в именовании меток. При желании разобраться в алгоритме - можно построчно сравнивать с кодом Goodwin98 - и разбираться с комментариями.
Алгоритм состоит в делении исходного числа на основание системы счисления, т.е. на 10 и печати остатков от этого деления - цифр. Единственно, при делении получаются цифры младших разрядов числа. Чтобы изменить порядок вывода цифр, в цикле деления на 10 остатки не выводятся на экран, а сохраняются в стек, после этого в дополнительном цикле цифры извлекаются из стека и выводятся на экран.

Assembler
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
;Вывод на экран целого 16 разрядного беззнакового числа
;на входе:
;  ax - целое 16 разрядное беззнаковое число
ShowUInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,     10              ;делитель (основание системы счисления)
        mov     cx,     0               ;количество выводимых цифр
        @@div:
                xor     dx,     dx      ;делим (dx:ax) на bx
                div     bx
                add     dl,     '0'     ;преобразуем остаток деления в символ цифры
                push    dx              ;и сохраняем его в стеке
                inc     cx              ;увеличиваем счётчик цифр
                test    ax,     ax      ;в числе ещё есть цифры?
        jnz     @@div                   ;да - повторить цикл выделения цифры
        @@show:
                mov     ah,     02h     ;функция ah=02h int 21h - вывести символ из dl на экран
                pop     dx              ;извлекаем из стека очередную цифру
                int     21h             ;и выводим её на экран
        loop    @@show                  ;и так поступаем столько раз, сколько нашли цифр в числе (cx)
    pop     dx
    pop     cx
    pop     bx
    pop     ax
        ret
ShowUInt16       endp
Применение
Assembler
        mov     ax, 12345 ;или иначе, поместив отображаемое число в ax
        call    ShowUInt16
Вывод целого 16-разрядного числа со знаком (DOS)

Как справедливо отметил в комментариях Goodwin98, вывод знакового и беззнакового чисел фактически одинаковы. Только перед выводом знакового, нужно один раз выполнить некоторые действия (взять абсолютное значение выводимого числа), а потом вывести обычное беззнаковое число (ведь после взятия абсолютного значения у нас - беззнаковое число). Что нужно добавить перед выводом:
- проверить знак выводимого числа
- если число отрицательное - вывести на экран символ "минус" и взять абсолютное значение числа
Итак, в итоге получим почти идентичную подпрограмму
Assembler
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
; выводит знаковое 16-разрядное число из регистра AX на экран
; на входе:
;  ax - число для отображения
; на выходе:
;  число на экране
ShowInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,     10      ;основание системы счисления (делитель)
        xor     cx,     cx      ;количество символов в модуле числа
        or      ax,     ax      ;для отрицательного числа
        jns     @@div
                neg     ax      ;поменять знак (сделать положительным)
                push    ax      ;и вывести на экран символ "-" (минус)
                mov     ah,     02h
                mov     dl,     '-'
                int     21h
                pop     ax
        @@div:                  ;делим число на 10
                xor     dx,     dx
                div     bx
                push    dx      ;остаток сохраняем в стеке
                inc     cx      ;количество цифр в числе
                or      ax,     ax
        jnz     @@div           ;повторяем, пока в числе есть цифры
        mov     ah,     02h
        @@store:
                pop     dx      ;извлекаем цифры (остатки от деления на 10) из стека
                add     dl,     '0'     ;преобразуем в символы цифр
                int     21h     ;и выводим их на экран
        loop    @@store
    pop     dx
    pop     cx
    pop     bx
    pop     ax
        ret
ShowInt16       endp
Применение
Assembler
        mov     ax, -12345 ;или иначе, поместив отображаемое число в ax
        call    ShowInt16
Вывод шестнадцатеричного числа (DOS)

При выводе по основанию системы счисления являющемуся степенью 2 деление и умножение выполняются при помощи битовых операций (сдвиг, И, ИЛИ), также можно отказаться от хранения остатков в стеке и преобразование вести не от младших цифр к старшим, а наоборот, т.к. выводимое число легко разделяется на независимые битовые поля (служащие остатками от деления на основание системы счисления). Но принцип преобразования остаётся неизменным.
1. Табличный метод с применением команды xlat
Как видно, сначала выполняется преобразование в строку (в переменную asHex), а потом эта строка выводится на экран. По хорошему, эту переменную бы объявить как локальную на стеке, а таблицу HexTabl - в сегменте данных. Но при обучении - достаточно преренести строки с объявлениями этих переменных в сегмент данных.
Assembler
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
;выводит, сначала в переменную, а затем на экран, в 16 системе счисления
;содержимое регистра AL
;для вывода на экран регистра AX нужно вызвать эту процедуру дважды
;       xchg al, ah
;       call ShowHex
;       xchg al, ah
;       call ShowHex
HexTabl  db     '0123456789abcdef'
asHex    db     '00', '$'
ShowHex PROC 
        pusha
        mov     di, OFFSET asHex
        mov     cx, ax
        and     ax, 000fh
        mov     bx, OFFSET HexTabl
        xlat
        mov     [di+1], al
 
        mov     ax, cx
        and     ax, 00f0h
        mov     cl, 4
        shr     ax, cl
        xlat
        mov     [di], al
 
        mov     ah, 09h
        mov     dx, OFFSET asHex
        int     21h
        popa
        ret
ShowHex ENDP
Применение
Assembler
        mov     ax, 1234h
        xchg    al, ah
        call    ShowHex
        xchg    al, ah
        call    ShowHex
2. Обычное вычисление (если остаток меньше 10, то прибавить к нему '0', а если больше - то прибавить к нему 'A'-10) каждого выводимого символа
Assembler
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
;выводит на экран в 16 системе счисления содержимое регистра AX
; входные данные:
; ax - число для отображения
ShowHex proc
        push    ax
        push    cx
        push    dx
 
        ; Начинаем перевод числа AX в строку
        mov    cl,      ((16-1)/4)*4    ; 16-битный регистр, будем выводить по 4 бита (0..F)
        xchg   dx,      ax              ; Сохраняем число в DX
 
@@Repeat:
 
        mov    ax,      dx              ; Восстанавливаем число в AX
        shr    ax,      cl              ; Сдвигаем на CL бит вправо
        and    al,      0Fh             ; Получаем в AL цифру 0..15
        add    al,      '0'             ; Получаем в AL символ цифры
        cmp    al,      '9'             ; Проверяем цифру
        jbe    @@Digit09                ; Прыгаем, если это цифра 0..9
        add    al,      'A'-('9'+1)     ; Иначе (для A..F) корректируем ее
 
@@Digit09:
 
        int    29h                      ; Выводим символ в AL на экран
        sub    cl,      4               ; Уменьшаем CL на 4 для следующей цифры
        jnc    @@Repeat                 ; Если знаковый CL >= 0, то повторяем
 
        pop     dx
        pop     cx
        pop     ax
        ret
ShowHex endp
Применение
Assembler
        mov     ax, 1234h
        call    ShowHex
3. "Хитрое" преобразование
Воспользовавшись тем, что вывод в 16 системе счисления это "особый случай" (основание есть степень 2) и возможностью использовать флаг переноса (заёма) CY совместно с командами сложения и вычитания с этим флагом (ADC, SBB), сверхоптимизаторы придумали такой код
Assembler
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
;выводит на экран в 16 системе счисления содержимое регистра AX
; входные данные:
; ax - число для отображения
ShowHex proc
        push    ax
        push    cx
        push    dx
 
        ; Начинаем перевод числа AX в строку
        mov     cl,     16-4    ; 16-битный регистр, будем выводить по 4 бита (0..F)
        xchg    dx,     ax      ; Сохраняем число в DX
 
@@Repeat:
 
        mov     ax,     dx      ; Восстанавливаем число в AX
        shr     ax,     cl      ; Сдвигаем на CL бит вправо
        and     al,     0Fh     ; Получаем в AL цифру 0..15
        add     al,     90h     ; special hex conversion sequence
        daa                     ; using ADDs and DAA's
        adc     al,     40h
        daa                     ; nibble now converted to ASCII
        int     29h             ; Выводим символ в AL на экран
        sub     cl,     4       ; Уменьшаем CL на 4 для следующей цифры
        jnc     @@Repeat        ; Если знаковый CL >= 0, то повторяем
 
        pop     dx
        pop     cx
        pop     ax
        ret
ShowHex endp
Другой вариант последовательности преобразования
Assembler
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
;выводит на экран в 16 системе счисления содержимое регистра AX
; входные данные:
; ax - число для отображения
ShowHex proc
        push    ax
        push    cx
        push    dx
 
        ; Начинаем перевод числа AX в строку
        mov     cl,     ((16-1)/4)*4    ; 16-битный регистр, будем выводить по 4 бита (0..F)
        xchg    dx,     ax      ; Сохраняем число в DX
 
@@Repeat:
 
        mov     ax,     dx      ; Восстанавливаем число в AX
        shr     ax,     cl      ; Сдвигаем на CL бит вправо
        and     al,     0Fh     ; обнуляем старшую тетраду
        cmp     al,     10      ; если al < 10, CF = 1
        sbb     al,     69h     ; если в al была цифра 0..9, тогда в al
                                ; будет 96h..9Fh, если в al была цифра 0Ah..0Fh,
                                ; тогда в al будет 0A1h..0A6h
        das                     ; 0-9: 96h..9Fh минус 66h -> 30h..39h,
                                ; A-F: 0A1h..0A6h минус 60h -> 41h..46h
        int     29h             ; Выводим символ в AL на экран
        sub     cl,     4       ; Уменьшаем CL на 4 для следующей цифры
        jnc     @@Repeat        ; Если знаковый CL >= 0, то повторяем
 
        pop     dx
        pop     cx
        pop     ax
        ret
ShowHex endp
Применение
Assembler
        mov     ax, 1A2Fh
        call    ShowHex
Похожее по смыслу преобразование приводит Goodwin98 в процедуре byte2hex.

К слову, в https://www.cyberforum.ru/post5356776.html в подразделе "Применение команды SBB" приводятся аналогичные примеры оптимизации.

Также, будет интересна подборка 27 (sic!) способов вывода чисел в шестнадцатеричном виде, подготовленная Mikl___:
Написать программу перевода двухбайтового целого числа в массив символов

Вывод двоичного 16-разрядного числа (DOS)

Приведённый код почти соответствует примеру Goodwin98 (OutBin), различие в способе преобразования выделенного бита в код цифры. Т.к. за кодом цифры '0' следует код цифры '1', то можно без ветвления и переходов сложить код символа '0' с числом 0 и значением флага переноса CY (после сдвига в этом флаге находится очередной бит выводимого числа).
Assembler
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
;выводит на экран в двоичной системе счисления содержимое регистра AX
; входные данные:
; ax - число для отображения
ShowBin proc
        push    ax
        push    bx
        push    cx
 
        mov     bx,     ax      ; из-за распределения регистров
                                ; при выводе на экран с помощью int 29h,
                                ; выводимое число будет находится в bx
        mov     cx,     16      ; переменная цикла равна количеству бит в слове
 
@@For:
 
        mov     ax,     '0'     ; в регистрах al=код символа '0', ah=00h
 
        shl     bx,     1       ; выделение бита
 
        adc     al,     ah      ; сложение кода символа '0' со значением выделенного бита
 
        int     29h
 
        loop    @@For
 
        pop     cx
        pop     bx
        pop     ax
        ret
ShowBin endp
Применение
Assembler
        mov     ax, 12345 ;или иначе, поместив отображаемое число в ax
        call    ShowBin
Вывод восьмеричного 16-разрядного числа (DOS)

Как видно, эта процедура почти полная копия варианта 2 вывода шестнадцатеричного представления числа.
Assembler
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
;выводит на экран в 8 системе счисления содержимое регистра AX
; входные данные:
; ax - число для отображения
ShowOct proc
        push    ax
        push    cx
        push    dx
 
        ; Начинаем перевод числа AX в строку
        mov    cl,      ((16-1)/3)*3    ; 16-битный регистр, будем выводить по 3 бита (0..7)
        mov    dx,      ax              ; Сохраняем число в DX
 
@@Repeat:
 
        mov    ax,      dx              ; Восстанавливаем число в AX
        shr    ax,      cl              ; Сдвигаем на CL бит вправо (делим на 8*i)
        and    al,      07h             ; Получаем в AL цифру 0..7 (остаток от деления на 8)
        add    al,      '0'             ; Получаем в AL символ цифры
 
        int    29h                      ; Выводим символ в AL на экран
        sub    cl,      3               ; Уменьшаем CL на 3 для следующей цифры
        jnc    @@Repeat                 ; Если знаковый CL >= 0, то повторяем
 
        pop     dx
        pop     cx
        pop     ax
        ret
ShowOct endp
Применение
Assembler
        mov     ax, 123456o ;или иначе, поместив отображаемое число в ax
        call    ShowOct
0
1615 / 785 / 236
Регистрация: 26.05.2012
Сообщений: 2,834
05.01.2017, 21:20 6
мой "подарок" форуму, перевод числа из 8-ой в 2-ую систему счисления. при запуске программы вводится число в 8-ой системе счисления, затем программа переводит это число в бинарную систему счисления и выводит
Assembler
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
129
130
131
132
133
134
135
136
137
138
139
140
.model small
.386
 
.data
var db 5 dup(?)
msg1 db 0Ah,0Dh,'input (oct): $'
msg2 db 0Ah,0Dh,'output (bin): $'
 
.stack
db 255 dup(?)
 
.code
start:
mov ax,@data
mov ds,ax
mov es,ax
 
mov ah,09h
lea dx,msg1
int 21h
 
lea di,var
xor cx,cx
input:
mov ah,01h
int 21h
 
cmp al,0Dh
je ready
 
cmp al,'7'
jbe skip
 
mov ah,02h
mov dl,08h
int 21h
 
mov ah,02h
mov dl,20h
int 21h
 
mov ah,02h
mov dl,08h
int 21h
 
jmp input
 
skip:
and al,0Fh
 
stosb
 
inc cx
 
cmp cx,5
je ready
jmp input
 
ready:
or cx,cx
je exit
 
push cx
 
lea si,var
xor di,di
xor ax,ax
 
lodsb
mov di,ax
 
dec cx
 
mov bx,8
collect:
or cx,cx
je two
 
mov ax,di
 
mul bx
 
mov di,ax
 
xor ah,ah
lodsb
 
add di,ax
 
dec cx
jmp collect
 
two:
pop cx
 
mov ax,3
 
mul cx
 
mov dx,ax
 
mov cx,16
sub cx,ax
 
mov bx,di
shl bx,cl
 
mov cx,dx
 
mov ah,09h
lea dx,msg2
int 21h
bit:
shl bx,1
jc one
 
zero:
mov ah,02h
mov dl,'0'
int 21h
 
jmp good
 
one:
mov ah,02h
mov dl,'1'
int 21h
 
good:
loop bit
 
exit:
mov ah,02h
mov dl,0Ah
int 21h
 
mov ah,4Ch 
mov al,00h
int 21h   
end start
1
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
05.01.2017, 22:33 7
Ввод чисел несколько сложнее вывода по причине возможности ошибочности входных данных (в строке присутствуют не только цифры или число в строке превышает разрядность выделяемой для него ячейки памяти).

При вводе чисел возможны несколько подходов - ввод символов совмещён с преобразованием и ввод символов осуществляется отдельно, преобразование строки в число отдельно. У каждого подхода свои достоинства и недостатки.

Ввод беззнакового числа в почти произвольной (не более 10) системе счисления
Это не совсем ввод числа, это преобразование строки в число. Непосредственно ввод строки нужно выполнить отдельной процедурой. На вход в процедуру Str2Num передаётся указатель на строку Pascal типа - т.е. по смещению 0 в строке находится её длина. Это достаточно удобно при работе в DOS, т.к. функция получения строки (ah=0Ah int 21h) возвращает не только строку но и её длину, причём сразу в нужной ячейке памяти.
Процедура преобразует строку в число предполагая 10 систему счисления. Чтобы изменить на другую систему, требуется закомментировать строку 24, а в регистре bx передать на вход предполагаемую систему счисления. Кроме того, для обеспечения корректности символов во входной строке требуется изменить в строке 36 верхний допустимый символ.
Опять же, как на одинаковые вопросы Природа даёт похожие ответы, так и эта процедура очень похожа на процедуру Goodwin98 InputInt. Разница в некоторых деталях: у меня не ввод числа, а преобразование строки, признак окончания строки в InputInt - специальный символ, а в моём случае, длина строки известна. Само же преобразование и проверки - близнецы-братья.
Assembler
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
; преобразования строки в число
; на входе:
; ds:[si] - строка с числом
; ds:[di] - адрес числа
; на выходе
; ds:[di] - число
; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
Str2Num proc
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
 
        push    ds
        pop     es
 
        mov     cl, ds:[si]
        xor     ch, ch
 
        inc     si
 
        mov     bx, 10
        xor     ax, ax
 
@@Loop:
        mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
        mov     [di], ax   ; игнорируем старшее слово
        cmp     dx, 0      ; проверяем, результат на переполнение
        jnz     @@Error
 
        mov     al, [si]   ; Преобразуем следующий символ в число
        cmp     al, '0'
        jb      @@Error
        cmp     al, '9'
        ja      @@Error
        sub     al, '0'
        xor     ah, ah
        add     ax, [di]
        jc      @@Error    ; Если сумма больше 65535
        inc     si
 
        loop    @@Loop
 
        mov     [di], ax
        clc
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
@@Error:
        xor     ax, ax
        mov     [di], ax
        stc
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Str2Num endp
Применение.
Пример применения получился сложнее, т.к. требовалось показать обработку ошибок преобразования строки в число в самой программе. Поэтому здесь приведу ссылку на пример из именно этих процедур.
https://www.cyberforum.ru/post9972970.html

Ввод знакового числа в почти произвольной (не более 10) системе счисления
К этой процедуре относятся все замечания, что и к процедуре для беззнакового преобразования.
Т.к. эта процедура лишь усложнение предыдущей, упомяну лишь отличия.
При обработке строки символов, содержащей знаковое число, первым обрабатывается возможный символ минус ('-'). Если он присутствует, то пока пропустим его.
Далее - ввод положительного числа.
Теперь, когда число введено, ещё раз посмотрим первый символ строки - признак отрицательного числа - на предмет наличия символа "минус". Если символ минус присутствует - инвертируем число.
Ко всем предыдущим проверкам добавляется ещё одна - после получения модуля вводимого числа, проверим модуль числа на неотрицательность, для исключения некорректного ввода числа
+3276810=1000'0000''0000'00002=-3276810
Причём, выполнять её нужно лишь для положительных чисел, т.к. для отрицательных после neg ax результат будет вполне корректный.
Эта же проверка дублируется в ходе преобразования - cmp ax, 8000h / ja @@Error.
Причина дублирования в несимметричности диапазона знаковых чисел (-32768...32767) - при вводе -32768 число корректное, а при вводе +32768 - уже нет. Поэтому в тексте присутствуют почти одинаковые проверки.
Assembler
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
; преобразования строки в число
; на входе:
; ds:[si] - строка с числом
; ds:[di] - адрес числа
; на выходе
; ds:[di] - число
; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
Str2Num PROC
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        push    si
 
        push    ds
        pop     es
 
        mov     cl, ds:[si]
        xor     ch, ch
 
        inc     si
 
        mov     bx, 10
        xor     ax, ax
 
        ;если в строке первый символ '-'
        ; - перейти к следующему
        ; - уменьшить количество рассматриваемых символов
        cmp     byte ptr [si], '-'
        jne     @@Loop
        inc     si
        dec     cx
@@Loop:
        mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
        mov     [di], ax   ; игнорируем старшее слово
        cmp     dx, 0      ; проверяем, результат на переполнение
        jnz     @@Error
 
        mov     al, [si]   ; Преобразуем следующий символ в число
        cmp     al, '0'
        jb      @@Error
        cmp     al, '9'
        ja      @@Error
        sub     al, '0'
        xor     ah, ah
        add     ax, [di]
        jc      @@Error    ; Если сумма больше 65535
        cmp     ax, 8000h
        ja      @@Error
        inc     si
 
        loop    @@Loop
 
        pop     si         ;проверка на знак
        push    si
        inc     si
        cmp     byte ptr [si], '-'
        jne     @@Check    ;если должно быть положительным
        neg     ax         ;если должно быть отрицательным
        jmp     @@StoreRes
@@Check:                   ;дополнительная проверка, когда при вводе положительного числа получили отрицательное
       or       ax, ax     ;
       js       @@Error
@@StoreRes:                ;сохранить результат
        mov     [di], ax
        clc
        pop     si
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
@@Error:
        xor     ax, ax
        mov     [di], ax
        stc
        pop     si
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Str2Num ENDP
Применение.
Из-за размеров, пример уберу под спойлер. Это не полноценная программа, а только фрагмент.
Кликните здесь для просмотра всего текста
Assembler
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
.DATA
 
KeyBuf  db      7, 0, 7 dup(0)      ;max,len,string,CR(0dh)
CR_LF   db      0Dh, 0Ah, '$'
 
Prompt  db      'Введите число (-32768..+32767): ', '$'
 
Error01 db      'Ошибка ввода числа',0Dh, 0Ah, '$'
 
Numer   dw      ?
 
.CODE
 
Main    PROC    FAR
..........................................
        ; ввод числа с клавиатуры (строки)
        lea     dx, Prompt
        mov     ah,09h
        int     21h
 
        mov     ah, 0Ah
        mov     dx, offset KeyBuf
        int     21h
 
        ; перевод строки (на новую строку)
        lea     dx, CR_LF
        mov     ah,09h
        int     21h
 
        ; преобразование строки в число
        lea     si, KeyBuf+1
        lea     di, Numer
        call    Str2Num
 
        ; проверка на ошибку
        jnc     @@NoError
 
        ; если есть ошибка ввода - напечатать сообщение об ошибке
        lea     dx, Error01
        mov     ah,09h
        int     21h
        jmp     @@Exit
 
        ; если нет ошибки ввода - напечатать число
@@NoError:
 
        mov     ax, Numer
        mov     cx, 10
        call    Show_AX
..........................................
Main    ENDP
 
        END     Main

Полный пример ввода и контрольного вывода введённого числа. При ошибке ввода числа программа повторяет ввод.
Кликните здесь для просмотра всего текста
Assembler
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
.model  small
 
.STACK  200h
 
.DATA
 
        KeyBuf          db      7, 0, 7 dup(0)      ;max,len,string,CR(0dh)
        CR_LF           db      0Dh, 0Ah, '$'
        Prompt          db      'Введите число (-32768..+32767): ', '$'
        Error01         db      'Ошибка ввода числа',0Dh, 0Ah, '$'
        Result          db      'Введено число: ', '$'
        msgPressAnyKey  db      0Dh, 0Ah, 'Press any key to exit...', '$'
 
        Numer           dw      ?
 
.code
 
; преобразования строки в число
; на входе:
; ds:[si] - строка с числом
; ds:[di] - адрес числа
; на выходе
; ds:[di] - число
; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
Str2Num PROC
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        push    si
 
        push    ds
        pop     es
 
        mov     cl, ds:[si]
        xor     ch, ch
 
        inc     si
 
        mov     bx, 10
        xor     ax, ax
 
        ;если в строке первый символ '-'
        ; - перейти к следующему
        ; - уменьшить количество рассматриваемых символов
        cmp     byte ptr [si], '-'
        jne     @@Loop
        inc     si
        dec     cx
@@Loop:
        mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
        mov     [di], ax   ; игнорируем старшее слово
        cmp     dx, 0      ; проверяем, результат на переполнение
        jnz     @@Error
 
        mov     al, [si]   ; Преобразуем следующий символ в число
        cmp     al, '0'
        jb      @@Error
        cmp     al, '9'
        ja      @@Error
        sub     al, '0'
        xor     ah, ah
        add     ax, [di]
        jc      @@Error    ; Если сумма больше 65535
        cmp     ax, 8000h
        ja      @@Error
        inc     si
 
        loop    @@Loop
 
        pop     si         ;проверка на знак
        push    si
        inc     si
        cmp     byte ptr [si], '-'
        jne     @@Check    ;если должно быть положительным
        neg     ax         ;если должно быть отрицательным
        jmp     @@StoreRes
@@Check:                   ;дополнительная проверка, когда при вводе положительного числа получили отрицательное
       or       ax, ax     ;
       js       @@Error
@@StoreRes:                ;сохранить результат
        mov     [di], ax
        clc
        pop     si
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
@@Error:
        xor     ax, ax
        mov     [di], ax
        stc
        pop     si
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Str2Num ENDP
 
; выводит знаковое 16-разрядное число из регистра AX на экран
; на входе:
;  ax - число для отображения
; на выходе:
;  число на экране
ShowInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,     10      ;основание системы счисления (делитель)
        xor     cx,     cx      ;количество символов в модуле числа
        or      ax,     ax      ;для отрицательного числа
        jns     @@div
                neg     ax      ;поменять знак (сделать положительным)
                push    ax      ;и вывести на экран символ "-" (минус)
                mov     ah,     02h
                mov     dl,     '-'
                int     21h
                pop     ax
        @@div:                  ;делим число на 10
                xor     dx,     dx
                div     bx
                push    dx      ;остаток сохраняем в стеке
                inc     cx      ;количество цифр в числе
                or      ax,     ax
        jnz     @@div           ;повторяем, пока в числе есть цифры
        mov     ah,     02h
        @@store:
                pop     dx      ;извлекаем цифры (остатки от деления на 10) из стека
                add     dl,     '0'     ;преобразуем в символы цифр
                int     21h     ;и выводим их на экран
        loop    @@store
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ShowInt16       endp
 
Main    PROC    FAR
        ;инициализация сегментного регистра ds адресом сегмента данных
        mov     ax,     @data
        mov     ds,     ax
 
@@Input:
        ; ввод числа с клавиатуры (строки)
        lea     dx,     [Prompt]
        mov     ah,     09h
        int     21h
 
        mov     ah,     0Ah
        mov     dx,     offset KeyBuf
        int     21h
 
        ; перевод строки (на новую строку)
        lea     dx,     [CR_LF]
        mov     ah,     09h
        int     21h
 
        ; преобразование строки в число
        lea     si,     [KeyBuf+1]
        lea     di,     [Numer]
        call    Str2Num
 
        ; проверка на ошибку
        jnc     @@NoError
 
        ; если есть ошибка ввода - напечатать сообщение об ошибке
        lea     dx,     [Error01]
        mov     ah,     09h
        int     21h
        jmp     @@Input ;и повторить ввод
 
        ; если нет ошибки ввода - напечатать число
@@NoError:
        mov     ah,     09h
        lea     dx,     [Result]
        int     21h
        mov     ax,     [Numer]
        mov     cx,     10
        call    ShowInt16
 
        ;ожидание нажатия любой клавиши
        mov     ah,     09h
        lea     dx,     [msgPressAnyKey]
        int     21h
        mov     ah,     00h
        int     16h
        ;завершение программы
        mov     ax,     4C00h
        int     21h
Main    ENDP
 
        END     Main

Чуть позже найду на форуме и добавлю ссылку на ввод числа, совмещённый со вводом с клавиатуры и фильтрацией "не цифр" от Constantin Cat. Я его видел, но сразу не могу найти. Он схож с процедурой Goodwin98 InputInt, отличие в именно фильтрации - ввод "не цифры" игнорируется. В разных случаях требуется различная реакция программы.
Ввод числа с фильтрацией ввода

А так же добавлю доработку своей процедуры для преобразования в знаковое целое.

Имеет смысл в закреплённой теме привести также ссылку на соответствующую главу https://www.cyberforum.ru/post6454877.html с несколькими примерами взаимных преобразований строк в числа. Они, правда, для разрядности x32 и выше, но могут также пригодится при невозможности использования функций API Windows или Linux.
2
4$M f0r3v3r XD
5821 / 1757 / 192
Регистрация: 14.12.2014
Сообщений: 3,298
Записей в блоге: 11
04.02.2017, 21:01 8
Преобразование 64-битного знакового числа в строку с использованием FPU (с помощью инструкции fbstp)

Написал несколько процедур для преобразования 64-битных знаковых чисел в строку с использованием FPU. Работа процедур основана на инструкции fbstp, которая записывает число в память в BCD-формате.
Для работы достаточно инструкций 286 процессора (или даже 8086, если удалить строку .286, однако я не рекомендую этого делать, т.к. код в этом случае неоправданно удлинится).

Привожу здесь 2 варианта процедур вывода 64-битного числа на стандартный вывод (обычно на экран) с помощью функции DOS ah=2/int 21h.

Итак, вариант вывода чисел размером не более 18 десятичных знаков (таковы ограничения работы инструкции fbstp), что примерно соответствует 61 биту (60 бит + знак) + пример использования:
Assembler
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
; ###############################################################################################################
; ## Преобразование 64-битных знаковых чисел в строку с использованием FPU - 2017 (c) Jin X                    ##
; ## Редакция, реализующая работу максимум с 18-знаковыми числами (что примерно соответствует 60-битам + знак) ##
; ###############################################################################################################
 
.model tiny
.286
.code
 
ifdef   ??Version               ; TASM
  smart
  locals
endif
 
.startup
 
        mov     si,offset Number
        call    FPUInt64Output  ; выводим число Number на стандартный вывод
        jc      @@Error         ; число с ошибкой?
    @@Finish:
        int     20h             ; выход из программы
    @@Error:
        mov     ah,9
        mov     dx,offset mError
        int     21h             ; выводим сообщение об ошибке
        jmp     @@Finish
 
; Преобразование 64-битного знакового числа, записанного по адресу SI, в строку с выводом на стандартный вывод (как правило, на экран; выводится не более 19 символов, включая знак минуса)
; Число должно содержать не более 18 десятичных цифр без учёта знака (т.е. меньше квинтиллиона по модулю, что по факту почти соответствует 60-битовому числу + знаковый бит)
; На выходе: флаг CF=0 в случае успеха, CF=1, если указано недопустимое число (больше 18 десятичных цифр), при этом ничего не выводится
FPUInt64Output  proc    c       ; не удаляйте слово 'c', иначе процедура работать не будет (не будет генерироваться пролог и эпилог)
local   BCD: tbyte              ; резервируем место в стеке для записи BCD-формата числа (при этом выполняется enter 10,0 или его эквивалент push bp + mov bp,sp + sub sp,10)
        fild    qword ptr [si]  ; читаем целое число по адресу si (если число записано в формате с плавающей запятой, fild нужно заменить на fld)
        fbstp   [BCD]           ; записываем число в BCD-формате во временную локальную переменную
        mov     ah,2            ; номер функции DOS для вывода символа на экран
        mov     dx,'0-'         ; dl = символ минуса (для вывода отрицательных чисел), dh = символ нуля (отсекаемый символ, чтобы отсекать ведущие нули, см. ниже)
        add     byte ptr [BCD+9],dl  ; последний записанный инструкцией fbstp символ (10-й по счёту) = 0, если число положительное; 80h, если отрицательное; 0FFh в случае ошибки
        jc      @@Exit          ; 0FFh+'-' > 255, произошёл перенос, ошибка! (CF=1)
        jns     @@NoSign        ; 0+'-' < 80h, знака нет
        int     21h             ; выводим минус (80h+'-' > 80h, знак есть)
    @@NoSign:
        mov     si,18/2         ; кол-во пар символов (9)
    @@Next:
        mov     bl,byte ptr [BCD+si-1]  ; bl = очередной байт (читаем в обратном порядке, т.к. старшие цифры инструкция fbstp записывает в старшие байты)
        mov     cx,2            ; кол-во цифр в 1 байте
    @@Ch:
        ror     bl,4            ; меняем местами старшую и младшую тетраду (половину байта из 4-х бит), т.к. старшая цифра записана в старшей тетраде
        mov     dl,bl           ; переносим цифру в dl
        and     dl,0Fh          ; оставляем только 4 младший бита регистра dl (удаляя цифру в старшей тетраде)
        add     dl,'0'          ; преобразуем цифру в символ
        cmp     dl,dh           ; если ещё не выведено ни одной цифры, dh = '0' (см. выше); таким образом, если ещё не было цифр или были только '0', а текущий символ тоже '0',
        je      @@NoOut         ; ...то не выводим его
        int     21h             ; иначе выводим (ah=2, dl=символ)
        mov     dh,-1           ; чтобы в следующий раз выводились любые символы (в т.ч. '0')
    @@NoOut:
        loop    @@Ch            ; выводим вторую цифру
        dec     si              ; уменьшаем смещение следующего байта
        jnz     @@Next          ; переходим к следующему символу, если смещение > 0
        test    dh,dh           ; был ли вывод хотя бы одного символа? (заодно устанавливаем CF=0)
        js      @@Exit          ; да (если dh=-1), переходим
        int     21h             ; иначе выводим ноль (ah=2, dl='0') - такая ситуация возникнет только если исходное число = 0
    @@Exit:
        ret                     ; выходим из процедуры (предварительно выполняется leave или его эквивалент mov sp,bp + pop bp)
FPUInt64Output  endp
 
.data
 
mError db       'Wrong number!',13,10,'$'
 
Number  dq      -1234567890775533
String  db      20 dup (?)
 
end
Усовершенствованный вариант вывода любых 64-битных чисел (ограничения инструкции fbstp ликвидируются предварительным вычислением старшей 19-й цифры с привидением числа к 18-знаковому) + пример использования:
Assembler
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
; ###############################################################################################################
; ## Преобразование 64-битных знаковых чисел в строку с использованием FPU - 2017 (c) Jin X                    ##
; ## Редакция, реализующая работу с любыми 64-битовыми числами (в т.ч. содержащими более 18 десятичных знаков) ##
; ###############################################################################################################
 
.model tiny
.286
.code
 
ifdef   ??Version               ; TASM
  smart
  locals
endif
 
.startup
 
        mov     si,offset Number
        call    FPUInt64Output  ; выводим число Number на стандартный вывод
 
        int     20h             ; выход из программы
 
; Преобразование 64-битного знакового числа, записанного по адресу SI, в строку с выводом на стандартный вывод (как правило, на экран; выводится не более 20 символов, включая знак минуса)
FPUInt64Output  proc    c       ; не удаляйте слово 'c', иначе процедура работать не будет (не будет генерироваться пролог и эпилог)
local   BCD: tbyte              ; резервируем место в стеке для записи BCD-формата числа (при этом выполняется enter 10,0 или его эквивалент push bp + mov bp,sp + sub sp,10)
        ; Поскольку инструкция fbstp может записать число в BCD-формате, состоящее максимум из 18 десятичных цифр, а 64-битное число может содержать ещё одну цифру, придётся выполнить ряд операций...
        fld     [Double1e18]    ; st(0) = 1e18 (квинтиллион, единица с 18 нулями)
        fild    qword ptr [si]  ; st(0) = целое число по адресу si (число должно быть записано как обычное целое, а не в формате с плавающей запятой), st(1) = 1e18
        mov     dx,'0-'         ; dl = символ минуса (для вывода отрицательных чисел), dh = символ нуля (отсекаемый символ, чтобы отсекать ведущие нули, см. ниже)
        test    byte ptr [si+7],80h  ; проверяем старший бит исходного числа
        jz      @@Chk1e18       ; если он сброшен, значит число положительное, прыгаем
 
        mov     ah,2            ; номер функции DOS для вывода символа на экран
        int     21h             ; выводим минус (ah=2, dl=символ)
        fchs                    ; меняем знак (преобразуем в положительное): st(0) = |st(0)|
        jmp     @@Chk1e18       ; переходим к сравнению числа с 1e18 (квинтиллионом)
    @@Sub1e18:
        inc     dx              ; увеличиваем dl на 1 (19-ю цифру)
        mov     dh,-1           ; dh = -1, чтобы в дальнейшем выводились все цифры (без удаления ведущих нулей)
        fsub    st(0),st(1)     ; st(0) = st(0)-st(1) = число-1e18
    @@Chk1e18:
        fcom                    ; сравниваем st(0) (число) с st(1) (1e18)
        fstsw   ax              ; получаем слово статуса FPU (результата сравнения)
        sahf                    ; сохраняем его во флагах
        jae     @@Sub1e18       ; если больше (число > 1e18), значит есть 19-й знак, прыгаем
 
        mov     ah,2            ; номер функции DOS для вывода символа на экран
        test    dh,dh           ; 19-я цифра не 0 (если она есть, dh=-1)?
        jns     @@No19          ; если 0, не выводим её
        add     dl,'0'-'-'      ; иначе преобразуем цифру в символ, добавив разницу между '-' и '0' (изначально мы прибавляли единицы к символу al='-')
        int     21h             ; и выводим его (ah=2, dl=символ)
    @@No19:
        fbstp   [BCD]           ; записываем число (не более 18 цифр) в BCD-формате во временную локальную переменную с удалением его из стека FPU, st(0) = 1e18
        fstp    st(0)           ; удаляем лишнее число (st(0)=1e18) из стека FPU
 
        ; Основной цикл вывода цифр
        mov     si,18/2         ; кол-во пар символов (9)
    @@Next:
        mov     bl,byte ptr [BCD+si-1]  ; bl = очередной байт (читаем в обратном порядке, т.к. старшие цифры инструкция fbstp записывает в старшие байты)
        mov     cx,2            ; кол-во цифр в 1 байте
    @@Ch:
        ror     bl,4            ; меняем местами старшую и младшую тетраду (половину байта из 4-х бит), т.к. старшая цифра записана в старшей тетраде
        mov     dl,bl           ; переносим цифру в dl
        and     dl,0Fh          ; оставляем только 4 младший бита регистра dl (удаляя цифру в старшей тетраде)
        add     dl,'0'          ; преобразуем цифру в символ
        cmp     dl,dh           ; если ещё не выведено ни одной цифры, dh = '0' (см. выше); таким образом, если ещё не было цифр или были только '0', а текущий символ тоже '0',
        je      @@NoOut         ; ...то не выводим его
        int     21h             ; иначе выводим (ah=2, dl=символ)
        mov     dh,-1           ; чтобы в следующий раз выводились любые символы (в т.ч. '0')
    @@NoOut:
        loop    @@Ch            ; выводим вторую цифру
 
        dec     si              ; уменьшаем смещение следующего байта
        jnz     @@Next          ; переходим к следующему символу, если смещение > 0
        test    dh,dh           ; был ли вывод хотя бы одного символа?
        js      @@Exit          ; да (если dh=-1), переходим
        int     21h             ; иначе выводим ноль (ah=2, dl='0') - такая ситуация возникнет только если исходное число = 0
    @@Exit:
        ret                     ; выходим из процедуры (предварительно выполняется leave или его эквивалент mov sp,bp + pop bp)
FPUInt64Output  endp
 
.data
 
Double1e18 dq   1.0e18          ; значение, используемое в процедурах
 
Number  dq      8000000000000000h  ; -9223372036854775808 (минимально возможное значение)
String  db      21 dup (?)
 
end
Кроме приведённых выше процедур для каждой из версий я сделал ещё по 2 процедуры: вывод на экран через int 29h и преобразование числа в строку без вывода на экран.
Всё это вы можете найти в прикреплённых исходниках, а также по ссылке: Преобразование 64-битных знаковых чисел в строку с использованием FPU
.
1
Вложения
Тип файла: zip IntStr64FPU.zip (6.1 Кб, 12 просмотров)
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
01.07.2017, 22:07 9
Вывод целого беззнакового 32-разрядного числа
Вывод (или преобразование в строку) 32-разрядного числа алгоритмически несколько сложнее вывода 16-разрядного числа, т.к. формально в 16-разрядной DOS недоступны 32-разрядные регистры и выполнить деление на 10 затруднительно.

Поясню - пусть нужно разделить число 123456789 на 10. "На бумаге" всё получается очень просто 123456789/10=12345678 (остаток 9). Помещая делимое в регистровую пару dx:ax, а делитель в bx после команды div bx получаем переполнение и аварийный останов программы, т.к. частное 12345678 не "умещается" в регистре ax.

Но любой школьник легко сможет выполнить подобное деление - "в столбик" на листе бумаги. Значит и нам ничто не мешает поступить подобным образом.

Т.е. мы можем взять не всё исходное число, а только его старшее слово и разделить на 10. В результате получим в ax частное, а в dx - остаток. Частное можно сохранить для будущих вычислений, а в регистр ax можно загрузить следующее слово исходного делимого. Опять получается регистровая пара dx:ax, которую можно делить на 10. Причём при делении не возникнет переполнения, т.к. это частное будет меньше слова.

Деление можно проиллюстрировать изображением
Название: div.png
Просмотров: 5199

Размер: 870 байт
Т.к. первоначальное число можно сохранить во временной переменной, и исходное значение этой переменной нигде больше не нужно, то в этой же переменной можно по мере деления на 10 сохранять и частичные частные для получения следующей цифры исходного числа.
Достаточно удобно организовать обращение к составляющим исходного числа как к массиву слов, организовав индексацию через регистр si.

Весь остальной код по смыслу напоминает предыдущие примеры - сохранение остатков в стеке и дальнейший их вывод в порядке, обратном получению.
Добавлю, что для визуального упрощения исходного кода используется усложнённый синтаксис директивы (макроса) proc, что приводит к необходимости объявления в начале программы не только модели памяти программы, но и модели соглашения о вызове процедур и передачи им параметров .model small, Pascal. В данном случае я выбрал Pascal.
Assembler
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
;Вывод на экран целого 32 разрядного беззнакового числа
;на входе:
;  dx:ax - целое 32 разрядное беззнаковое число
ShowUInt32      proc
USES    ax,bx,cx,dx,si,di
LOCAL   temp:DWORD      ;переменная для сохранения частного от деления на 10
 
        ;сохранить значение числа во временной переменной
        mov     word ptr temp[0],       ax
        mov     word ptr temp[2],       dx
 
        mov     bx,     10      ;делитель
        mov     di,     0       ;количество цифр
@@NextDigit:
        mov     cx,     2       ;количество операций деления
        mov     si,     2       ;смещение делимого в переменной temp
        mov     dx,     0       ;остаток от предыдущего деления на 10
@@DivBy10:
        mov     ax,     word ptr temp[si]
        div     bx
        mov     word ptr temp[si],      ax
        sub     si,     2
        loop    @@DivBy10
        ;преобразование остатка от деления temp на 10 в символ цифры
        add     dl,     '0'
        push    dx              ;сохранение символа цифры в стеке
 
        inc     di              ;счётчик цифр увеличить на 1
 
        mov     ax,     word ptr temp[0]        ;остаток равен 0?
        or      ax,     word ptr temp[2]
        jnz     @@NextDigit                     ;нет - продолжить
 
        ;вывод цифр
        mov     cx,     di      ;количество выводимых символов
        mov     ah,     02h     ;функция вывода на экран для int 21h
@@ShowDigit:
        pop     dx              ;извлечение цифры из стека
        int     21h             ;вывод символа на экран
        loop    @@ShowDigit
 
        ret
ShowUInt32      endp
Пример использования
Assembler
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
LOCALS
 
.model small, Pascal
 
.stack 100h
 
.data
        ui32    dd      1234567890
 
.code
 
main    proc
        mov     ax,     @data
        mov     ds,     ax
 
        mov     ax,     word ptr [ui32]
        mov     dx,     word ptr [ui32+2]
        call    ShowUInt32
 
        mov     ax,     4C00h
        int     21h
main    endp
 
;Вывод на экран целого 32 разрядного беззнакового числа
;на входе:
;  dx:ax - целое 32 разрядное беззнаковое число
ShowUInt32      proc
.......................
ShowUInt32      endp
 
end     main
P. S. В целом, данный код представляет интерес ещё и с точки зрения расширяемости для вывода 64 и более разрядных чисел.

P. P. S. Также, будет интересна подборка способов ввода и вывода 32 и 64 разрядных чисел в десятичном виде, подготовленная Mikl___:
Остаток от деления на 10 32-битного числа в dx:ax
https://www.cyberforum.ru/post6454877.html
2
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
19.01.2019, 17:54 10
Вывод результата деления целых 16-разрядных чисел в виде десятичной дроби

Сразу отмечу — в статье рассмотрю только вывод на экран результата деления, а не преобразование числа в другой формат с сохранением в ячейках памяти. Это означает, что при помощи кода из статьи невозможно, к примеру, сложить две рациональные дроби путём получения двух десятичных дробей и выполнения их сложения.

Результатом деления двух целых чисел инструкциями div и idiv является частное и остаток. При необходимости вывода результата в виде десятичной дроби потребуется дополнительная обработка.
Пусть необходимо вывести результат деления двух чисел A и B. Проведём преобразования
https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{A}{B}=C+\frac{D}{B}
где C — частное (целая часть дроби),
D — остаток от деления, сразу отметим, что справедливо D<B.
Получается, что на экран нужно вывести число C, десятичный разделитель и цифры дробной части. Вывод частного не должен вызывать затруднений, т.к. уже тщательно рассмотрен в статьях топика.

Рассмотрим алгоритм на примере деления числа 4987 на 123. При обычном делении в столбик получим
Ввод и вывод чисел в различных системах счисления
После получения целой части результата - имеем частное 40 и остаток 67. Продолжим деление: к результату дописываем десятичный разделитель (запятую), к остатку (теперь это новое делимое) справа дописываем цифру ноль (умножаем на 10) и пробуем разделить на делитель. Получаем первую цифру дробной части результата (цифру 5) и новый остаток (число 55). Продолжая по такому же принципу получим ещё три цифры в десятичной части результата.

Сформулируем алгоритм:
1. Делением получаем частное и выводим его любым известным способом.
2. Выводим символ десятичного разделителя (запятую).
3. В цикле, требуемое число раз, выводим по одному символу цифры:
3.1 Остаток умножаем на 10.
3.2 Результат умножения делим на делитель
3.3 Новое частное (это всегда одна цифра) выводим на экран

Для демонстрации, рассмотрим реализацию этого алгоритма на языках Pascal и C. Реализация по сравнению с алгоритмом немного усложняется: нет смысла выводить цифры десятичной части при нулевом остатке, да и саму дробную часть при делении исходных чисел без остатка.
Реализация алгоритма на Pascal
Pascal
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
var
  a, b: integer;
  i: integer;
  Accuracy: integer;
begin
  a := 4987;      {делимое}
  b := 123;       {делитель}
  Accuracy := 10; {количество выводимых цифр дробной части}
  {вывод частного - целой части деления}
  Write(a, '/', b, '=', a div b);
  a := a mod b;   {при делении получаем и остаток}
  if a <> 0 then  {если остаток ненулевой,}
  begin           {то будем выводить и цифры дробной части}
    Write('.');   {вывод десятичного разделителя}
    for i := 1 to Accuracy do
    begin
      if a = 0 then
        break;
      a := a * 10;
      Write(a div b); {вывод следующей цифры}
      a := a mod b;
    end;
  end;
  writeln;
end.

Реализация алгоритма на C
C
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
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int a=4987;       //делимое
    int b=123;        //делитель
    int Accuracy=10;  //количество выводимых цифр дробной части
    //вывод частного - целой части деления
    printf("%d / %d = %d", a, b, a/b);
    a = a%b;          //при делении получаем и остаток
    if(a)             //если остаток ненулевой,
    {                 //то будем выводить и цифры дробной части
        printf(".");  //вывод десятичного разделителя
        for(int i=Accuracy; i; i--)
        {
            if(!a)
                break;
            a=a*10;
            printf("%d", a/b); //вывод следующей цифры
            a=a%b;
        };
        return 0;
    }
}

Реализуем на языке ассемблера, оформив в виде процедуры.
Рассмотрим два случая, деление беззнаковых чисел и деление чисел со знаком.

Для беззнаковых чисел.
В процедуре используется вызов подпрограммы ShowUInt16 для вывода содержимого регистра ax, т.к. вывод частного всё равно будет полностью повторять её и нет смысла дублировать код, тем более, что в реальной программе вывод числа всё равно используется в другом участке кода. При выводе каждой цифры дроби можно было бы уже не вызывать ShowUInt16 и воспользоваться упрощённым кодом, т.к. выводится будет всего одна цифра. Я решил в обоих случаях вызывать ShowUInt16 для наглядности.
Assembler
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
;Вывод на экран десятичную дробь - результат деления двух чисел
;на входе:
;  ax - делимое (целое 16 разрядное беззнаковое число)
;  bx - делитель (целое 16 разрядное беззнаковое число)
;  cx - количество цифр в дробной части выводимого числа
ShowFraction    proc
        push    ax
        push    cx
        push    dx
        ;вывод частного - целой части деления
        xor     dx,     dx
        div     bx
        call    ShowUInt16
                                ;при делении получаем и остаток в dx
        or      dx,     dx      ;если остаток ненулевой,
        jz      @@skip          ;то будем выводить и цифры дробной части
                push    dx      ;вывод десятичного разделителя
                mov     ah,     02h
                mov     dl,     ','
                int     21h
                pop     dx
                ;вывод нескольких десятичных знаков десятичной дроби
                mov     si,     10      ;основание счисления
                ;в cx - количество знаков после запятой (входные данные)
                @@for:
                        mov     ax,     dx
                        xor     dx,     dx
                        mul     si
                        div     bx
                        call    ShowUInt16      ;вывод следующей цифры
                loop    @@for
        @@skip:
        pop     dx
        pop     cx
        pop     ax
        ret
ShowFraction    endp
Пример использования
Assembler
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
.model small
 
.stack 200h
 
.data
        CrLf            db      0Dh, 0Ah, '$'
        msgResult       db      'Result array:', 0Dh, 0Ah, '$'
        msgPressAnyKey  db      0Dh, 0Ah, 'Press any key to exit...', '$'
.code
 
;Вывод на экран целого 16 разрядного беззнакового числа
;на входе:
;  ax - целое 16 разрядное беззнаковое число
ShowUInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,     10
        xor     cx,     cx      ;символов (цифр) в числе
        @@div:
                inc     cx      ;количество цифр в числе
                xor     dx,     dx
                div     bx
                push    dx
                or      ax,     ax
        jnz     @@div
        mov     ah,     02h
        @@store:
                pop     dx
                add     dl,     '0'
                int     21h
        loop    @@store
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ShowUInt16       endp
 
;Вывод на экран десятичную дробь - результат деления двух чисел
;на входе:
;  ax - делимое (целое 16 разрядное беззнаковое число)
;  bx - делитель (целое 16 разрядное беззнаковое число)
;  cx - количество цифр в дробной части выводимого числа
ShowFraction    proc
        push    ax
        push    cx
        push    dx
        ;вывод частного - целой части деления
        xor     dx,     dx
        div     bx
        call    ShowUInt16
                                ;при делении получаем и остаток в dx
        or      dx,     dx      ;если остаток ненулевой,
        jz      @@skip          ;то будем выводить и цифры дробной части
                push    dx      ;вывод десятичного разделителя
                mov     ah,     02h
                mov     dl,     ','
                int     21h
                pop     dx
                ;вывод нескольких десятичных знаков десятичной дроби
                mov     si,     10      ;основание счисления
                ;в cx - количество знаков после запятой (входные данные)
                @@for:
                        mov     ax,     dx
                        xor     dx,     dx
                        mul     si
                        div     bx
                        call    ShowUInt16      ;вывод следующей цифры
                loop    @@for
        @@skip:
        pop     dx
        pop     cx
        pop     ax
        ret
ShowFraction    endp
 
main    proc
        ;инициализация сегментного регистра ds адресом сегмента данных
        mov     ax,     @data
        mov     ds,     ax
 
        ;вывод результата деления
        mov     ax,     4987    ;делимое
        mov     bx,     123     ;делитель
        mov     cx,     10      ;количество выводимых цифр дробной части
        call    ShowFraction
 
        ;ожидание нажатия любой клавиши
        mov     ah,     09h
        lea     dx,     [msgPressAnyKey]
        int     21h
 
        mov     ah,     00h
        int     16h
 
        ;завершение программы
        mov     ax,     4C00h
        int     21h
main    endp
 
end     main

Для чисел со знаком изменится:
1. В строки 11-13 будут
Assembler
11
12
13
        cwd
        idiv     bx
        call    ShowSInt16
2. Между строками 16 и 17 нужно будет вставить код взятия абсолютных значений регистров bx и dx - перед получением десятичной части необходимо взять абсолютные значения остатка и делителя.
3. В строке 30 вызывать процедуру ShowSInt16.
И в результате получится:
Assembler
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
;Вывод на экран десятичной дроби - результата деления двух чисел
;на входе:
;  ax - делимое (целое 16 разрядное число со знаком)
;  bx - делитель (целое 16 разрядное число со знаком)
;  cx - количество цифр в дробной части выводимого числа
ShowFraction    proc
        push    ax
        push    cx
        push    dx
        ;вывод частного - целой части деления
        cwd
        idiv    bx
        call    ShowSInt16
                                ;при делении получаем и остаток в dx
        or      dx,     dx      ;если остаток ненулевой,
        jz      @@skip          ;то будем выводить и цифры дробной части
                or      bx,     bx
                jns     @@1
                        neg     bx
                @@1:
                or      dx,     dx
                jns     @@2
                        neg     dx
                @@2:
                push    dx      ;вывод десятичного разделителя
                mov     ah,     02h
                mov     dl,     ','
                int     21h
                pop     dx
                ;вывод нескольких десятичных знаков десятичной дроби
                mov     si,     10      ;основание счисления
                ;в cx - количество знаков после запятой (входные данные)
                @@for:
                        mov     ax,     dx
                        xor     dx,     dx
                        mul     si
                        div     bx
                        call    ShowSInt16      ;вывод следующей цифры
                loop    @@for
        @@skip:
        pop     dx
        pop     cx
        pop     ax
        ret
ShowFraction    endp
Пример использования
Assembler
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
.model small
 
.stack 200h
 
.data
        CrLf            db      0Dh, 0Ah, '$'
        msgResult       db      'Result array:', 0Dh, 0Ah, '$'
        msgPressAnyKey  db      0Dh, 0Ah, 'Press any key to exit...', '$'
.code
 
;Вывод на экран целого 16 разрядного числа со знаком
;на входе:
;  ax - целое 16 разрядное число со знаком
ShowSInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,     10
        xor     cx,     cx      ;символов в модуле числа
        or      ax,     ax
        jns     @@div
                neg     ax
                push    ax
                mov     ah,     02h
                mov     dl,     '-'
                int     21h
                pop     ax
        @@div:
                xor     dx,     dx
                div     bx
                push    dx
                inc     cx      ;количество цифр в числе
                or      ax,     ax
        jnz     @@div
        mov     ah,     02h
        @@store:
                pop     dx
                add     dl,     '0'
                int     21h
        loop    @@store
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ShowSInt16       endp
 
;Вывод на экран десятичной дроби - результата деления двух чисел
;на входе:
;  ax - делимое (целое 16 разрядное число со знаком)
;  bx - делитель (целое 16 разрядное число со знаком)
;  cx - количество цифр в дробной части выводимого числа
ShowFraction    proc
        push    ax
        push    cx
        push    dx
        ;вывод частного - целой части деления
        cwd
        idiv    bx
        call    ShowSInt16
                                ;при делении получаем и остаток в dx
        or      dx,     dx      ;если остаток ненулевой,
        jz      @@skip          ;то будем выводить и цифры дробной части
                or      bx,     bx
                jns     @@1
                        neg     bx
                @@1:
                or      dx,     dx
                jns     @@2
                        neg     dx
                @@2:
                push    dx      ;вывод десятичного разделителя
                mov     ah,     02h
                mov     dl,     ','
                int     21h
                pop     dx
                ;вывод нескольких десятичных знаков десятичной дроби
                mov     si,     10      ;основание счисления
                ;в cx - количество знаков после запятой (входные данные)
                @@for:
                        mov     ax,     dx
                        xor     dx,     dx
                        mul     si
                        div     bx
                        call    ShowSInt16      ;вывод следующей цифры
                loop    @@for
        @@skip:
        pop     dx
        pop     cx
        pop     ax
        ret
ShowFraction    endp
 
main    proc
        ;инициализация сегментного регистра ds адресом сегмента данных
        mov     ax,     @data
        mov     ds,     ax
 
        ;вывод результата деления
        mov     ax,     4987    ;делимое
        mov     bx,     -123    ;делитель
        mov     cx,     10      ;количество выводимых цифр дробной части
        call    ShowFraction
 
        ;ожидание нажатия любой клавиши
        mov     ah,     09h
        lea     dx,     [msgPressAnyKey]
        int     21h
 
        mov     ah,     00h
        int     16h
 
        ;завершение программы
        mov     ax,     4C00h
        int     21h
main    endp
 
end     main


Код, представленный здесь, рабочий, но предназначен для пояснения алгоритма, а не к сокращению размера и высокой эффективности. Кроме того, желание оставаться в рамках совместимости с эмулятором emu8086, не поддерживающим множество функций DOS (например, int 29h) тоже не позволяет уменьшить размер.

Примеры других реализаций алгоритма
https://www.cyberforum.ru/post13252664.html
Вычислить значение выражения
https://www.cyberforum.ru/post10592976.html
Вычислите значение кусочной функции
1
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
12.06.2020, 18:47 11
Как пользоваться процедурами ввода и вывода чисел

Исходное состояние программы
Программа пользователя, в которой требуется применение процедур ввода или вывода, имеет примерно следующий вид
Предположим, вычисление по формуле Y=X+Z
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.model tiny
.code
        org     100h
main    proc
        jmp     start
        ;Данные
        X       dw      ?
        Y       dw      ?
        Z       dw      ?
start:
        ;программа
        mov     ax,     [X]
        add     ax,     [Z]
        mov     [Y],    ax
        ;завершение программы
        int     20h
main    endp
 
end     main
Добавление вывода числа
В сегмент кода копируем процедуру ShowInt16, а сразу после вычислений вызываем её.
Чтобы сократить объём текста в сообщении, а также предоставить возможность потренироваться в копировании процедуры, в исходнике не буду показывать содержимое процедуры ShowInt16, только обозначу её месторасположение.
Assembler
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
.model tiny
.code
        org     100h
main    proc
        jmp     start
        ;Данные
        X               dw      ?
        Y               dw      ?
        Z               dw      ?
        asResult        db      "Y=", '$'
start:
        ;программа
        mov     ax,     [X]
        add     ax,     [Z]
        mov     [Y],    ax
        ;вывод результата на экран
        mov     ah,     09h
        lea     dx,     [asResult]
        int     21h
        mov     ax,     [Y]
        call    ShowInt16
        ;завершение программы
        int     20h
main    endp
 
; выводит знаковое 16-разрядное число из регистра AX на экран
; на входе:
;  ax - число для отображения
; на выходе:
;  число на экране
ShowInt16       proc
.......................................
суда копируется процедура вывода числа
любая одна из множества представленных
в данной теме
.......................................
ShowInt16       endp
 
end     main
Добавление ввода числа
Точно также выполняется копирование и вставка процедур ввода числа.
К некоторым процедурам ввода приводятся примеры ввода. Копируем их вместе с процедурой.
Так, для Str2Num получаем
Assembler
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
.model tiny
.code
        org     100h
main    proc
        jmp     start
        ;Данные
        X               dw      ?
        Y               dw      ?
        Z               dw      ?
        asResult        db      "Y=", '$'
        asPromptX       db      'Input X (-32768..+32767): ', '$'
        Error01         db      'Error entering a number',0Dh, 0Ah, '$'
        asCrLf          db      0Dh, 0Ah, '$'
        KeyBuf          db      7, 0, 7 dup(0)      ;max,len,string,CR(0dh)
start:
        ;программа
        ;ввод числа
        ; ввод числа X с клавиатуры (строки)
@@InputX:
        lea     dx,     [asPromptX]
        mov     ah,     09h
        int     21h
        mov     ah,     0Ah
        lea     dx,     [KeyBuf]
        int     21h
        ; перевод строки (на новую строку)
        mov     ah,     09h
        lea     dx,     asCrLf
        int     21h
        ; преобразование строки в число
        lea     si,     KeyBuf+1
        lea     di,     [X]
        call    Str2Num
        ; проверка на ошибку
        jnc     @@NoError
                ; если есть ошибка ввода - напечатать сообщение об ошибке
                lea     dx, Error01
                mov     ah,09h
                int     21h
        jmp     @@InputX        ;повторить ввод
 
        ; если нет ошибки ввода - напечатать число
@@NoError:
        ;вычисления
        mov     ax,     [X]
        add     ax,     [Z]
        mov     [Y],    ax
        ;вывод результата на экран
        mov     ah,     09h
        lea     dx,     [asResult]
        int     21h
        mov     ax,     [Y]
        call    ShowInt16
        ;завершение программы
        int     20h
main    endp
 
; выводит знаковое 16-разрядное число из регистра AX на экран
; на входе:
;  ax - число для отображения
; на выходе:
;  число на экране
ShowInt16       proc
.......................................
суда копируется процедура вывода числа
любая одна из множества представленных
в данной теме
.......................................
ShowInt16       endp
 
; преобразования строки в число
; на входе:
; ds:[si] - строка с числом
; ds:[di] - адрес числа
; на выходе
; ds:[di] - число
; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
Str2Num PROC
.......................................
суда копируется процедура ввода числа
.......................................
Str2Num ENDP
 
end     main
Аналогичным образом копируются и используются и другие процедуры ввода и вывода.
Помимо описания, для многих процедур показаны примеры их использования.
1
Модератор
Эксперт по электронике
7425 / 3640 / 1428
Регистрация: 01.02.2015
Сообщений: 11,337
Записей в блоге: 2
04.10.2020, 17:35 12
Вывод целых чисел для 32-разрядных программ на NASM

Введение

Ввод и вывод чисел для программ, написанных для NASM, рассмотрим отдельно из-за разнообразия возможных способов.
Возможные способы:
  • преобразование в строку с последующим выводом строки средствами операционной системы;
  • использование учебной IDE SASM позволяет использовать макросы, поставляемые с этой оболочкой;
  • компоновка при помощи gcc позволяет использовать библиотеку libc для ввода и вывода;
  • компоновка в Windows без сторонних библиотек (при помощи alink, golink, link или другого компоновщика) позволяет использовать функции WinAPI для ввода и вывода чисел;
  • компоновка в Windows без сторонних библиотек (при помощи alink, golink, link или другого компоновщика) позволяет использовать функции библиотеки msvcrt для ввода и вывода чисел.

Вывод целых чисел

Преобразование в строку с последующим выводом строки средствами операционной системы

Преобразование выполняется по следующему алгоритму — число делится на 10 (основание системы счисления), остаток преобразуется в символ цифры (путём сложения с кодом символа «0»), затем остаток сохраняется в стеке, после того, как частное становится равным нулю, деление превращается и извлекаем из стека цифры, которые помещаем в строку.
Для числа со знаком вначале ещё выполняется проверка знака числа, и для отрицательного — в строку помещается символ «-», а выводимое число заменяется его абсолютным значением.

Вывод 32-разрядного числа без знака (для Linux)
Assembler
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
;Процедура вывода беззнакового числа 32-разрядного (uint32) в консоль
;на входе:
;  eax - число для вывода
ShowUInt32:
        push    edi
        push    ecx
        push    ebx
 
        ;преобразование числа в строку
        ;строка сохраняется в глобальной переменной buf,
        ;а её длина - в переменной buflen
        lea     edi,    [buf]
        xor     ecx,    ecx
        mov     ebx,    10
        .do:
                xor     edx,    edx
                div     ebx
                add     dl,     '0'     ;преобразуем число (от 0 до 9) в символ цифры
                push    edx             ;заносим в стек для последующего вывода в порядке слева направо
                inc     ecx
                test    eax,    eax
        jnz     .do
        mov     [buflen],ecx            ;запоминаем длину строки
        .store:
                pop     eax             ;извлекаем символ цифры
                stosb                   ;сохраняем его в строке и увеличиваем адрес указателя (edi) на 1
        loop .store
 
        ;вывод строки в консоль средствами операционной системы Linux
        mov edx, [buflen]               ;message length
        mov ecx, buf                    ;message to write
        mov ebx, 1                      ;file descriptor (stdout)
        mov eax, 4                      ;system call number (sys_write)
        int 0x80                        ;call kernel
 
        pop ebx
        pop ecx
        pop edi
ret
Вывод 32-разрядного числа со знаком (для Linux)
Assembler
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
;Процедура вывода 32-разрядного числа со знаком (int32) в консоль
;на входе:
;  eax - число для вывода
ShowInt32:
        push    edi
        push    ecx
        push    ebx
 
        ;преобразование числа в строку
        ;строка сохраняется в глобальной переменной buf,
        ;а её длина - в переменной buflen
        lea     edi,    [buf]
        xor     ecx,    ecx
        mov     [buflen],ecx
        mov     ebx,    10
        test    eax,    eax             ;если число отрицательное, то
        jns     .do                     ;поместим в строку символ "-"
                neg     eax             ;и возьмём абсолютное значение
                mov     [edi],  byte '-'
                mov     [buflen],       byte 1
                inc     edi
        .do:
                xor     edx,    edx
                div     ebx
                add     dl,     '0'     ;преобразуем число (от 0 до 9) в символ цифры
                push    edx             ;заносим в стек для последующего вывода в порядке слева направо
                inc     ecx
                test    eax,    eax
        jnz     .do
        add     [buflen],ecx            ;запоминаем длину строки
        .store:
                pop     eax             ;извлекаем символ цифры
                stosb                   ;сохраняем его в строке и увеличиваем адрес указателя (edi) на 1
        loop .store
 
        ;вывод строки в консоль средствами операционной системы Linux
        mov edx, [buflen]               ;message length
        mov ecx, buf                    ;message to write
        mov ebx, 1                      ;file descriptor (stdout)
        mov eax, 4                      ;system call number (sys_write)
        int 0x80                        ;call kernel
 
        pop ebx
        pop ecx
        pop edi
ret
Пример использования
проверено в онлайн компиляторе
https://www.tutorialspoint.com... online.php
компиляция командной строкой
Bash
1
nasm -f elf *.asm; ld -m elf_i386 -s -o demo *.o
Кликните здесь для просмотра всего текста
Assembler
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
section .text
 
        global _start           ;must be declared for using gcc
_start:                         ;tell linker entry point
 
        mov     eax,    [x]
        call    ShowUInt32
 
        call    NewLine
 
        mov     eax,    [y]
        call    ShowUInt32
 
        call    NewLine
 
        mov     eax,    [z]
        call    ShowInt32
 
        mov     eax,    1       ;system call number (sys_exit)
        int     0x80            ;call kernel
 
;Процедура вывода беззнакового числа 32-разрядного (uint32) в консоль
;на входе:
;  eax - число для вывода
ShowUInt32:
        push    edi
        push    ecx
        push    ebx
 
        ;преобразование числа в строку
        ;строка сохраняется в глобальной переменной buf,
        ;а её длина - в переменной buflen
        lea     edi,    [buf]
        xor     ecx,    ecx
        mov     ebx,    10
        .do:
                xor     edx,    edx
                div     ebx
                add     dl,     '0'     ;преобразуем число (от 0 до 9) в символ цифры
                push    edx             ;заносим в стек для последующего вывода в порядке слева направо
                inc     ecx
                test    eax,    eax
        jnz     .do
        mov     [buflen],ecx            ;запоминаем длину строки
        .store:
                pop     eax             ;извлекаем символ цифры
                stosb                   ;сохраняем его в строке и увеличиваем адрес указателя (edi) на 1
        loop .store
 
        ;вывод строки в консоль средствами операционной системы Linux
        mov edx, [buflen]               ;message length
        mov ecx, buf                    ;message to write
        mov ebx, 1                      ;file descriptor (stdout)
        mov eax, 4                      ;system call number (sys_write)
        int 0x80                        ;call kernel
 
        pop ebx
        pop ecx
        pop edi
ret
 
;Процедура вывода 32-разрядного числа со знаком (int32) в консоль
;на входе:
;  eax - число для вывода
ShowInt32:
        push    edi
        push    ecx
        push    ebx
 
        ;преобразование числа в строку
        ;строка сохраняется в глобальной переменной buf,
        ;а её длина - в переменной buflen
        lea     edi,    [buf]
        xor     ecx,    ecx
        mov     [buflen],ecx
        mov     ebx,    10
        test    eax,    eax             ;если число отрицательное, то
        jns     .do                     ;поместим в строку символ "-"
                neg     eax             ;и возьмём абсолютное значение
                mov     [edi],  byte '-'
                mov     [buflen],       byte 1
                inc     edi
        .do:
                xor     edx,    edx
                div     ebx
                add     dl,     '0'     ;преобразуем число (от 0 до 9) в символ цифры
                push    edx             ;заносим в стек для последующего вывода в порядке слева направо
                inc     ecx
                test    eax,    eax
        jnz     .do
        add     [buflen],ecx            ;запоминаем длину строки
        .store:
                pop     eax             ;извлекаем символ цифры
                stosb                   ;сохраняем его в строке и увеличиваем адрес указателя (edi) на 1
        loop .store
 
        ;вывод строки в консоль средствами операционной системы Linux
        mov edx, [buflen]               ;message length
        mov ecx, buf                    ;message to write
        mov ebx, 1                      ;file descriptor (stdout)
        mov eax, 4                      ;system call number (sys_write)
        int 0x80                        ;call kernel
 
        pop ebx
        pop ecx
        pop edi
ret
 
;Процедура перевода строки
NewLine:
        mov     [buf],  byte 0Ah        ;код управляющего символа "перевод строки"
        mov     [buflen], dword 1       ;занимает 1 байт
        mov     edx,    [buflen]        ;message length
        mov     ecx,    buf             ;message to write
        mov     ebx,    1               ;file descriptor (stdout)
        mov     eax,    4               ;system call number (sys_write)
        int     0x80                    ;call kernel
ret
 
section .data
        x       dd      12345678
        y       dd      987654321
        z       dd      -123
section .bss
        buflen  resd    1               ;длина строки в буфере
        buf     resb    1024            ;буфер для ввода и вывода сообщений


Макросы IDE SASM

Для вывода чисел библиотека "io.inc" предлагает макросы PRINT_UDEC, PRINT_DEC, PRINT_HEX.
МакросОписание
PRINT_UDEC size, dataВывод числовых данных заданных параметром data в 10-чном представлении. Параметр size – число, указывающее размерность данность в байтах; допускаются значения 1, 2, 4, 8 (x64). В качестве параметра data может выступать числовая константа, символьная константа, имя переменной, имя регистра или адресное выражение (без спецификатора размера данных в памяти). Если задается регистр большего размера, то берется заданное параметром size количество младших разрядов. PRINT_UDEC интерпретирует число как беззнаковое, PRINT_DEC — как знаковое.
PRINT_DEC size, dataАналогично предыдущему, но PRINT_UDEC интерпретирует число как беззнаковое, PRINT_DEC — как знаковое.
PRINT_HEX size, dataАналогично предыдущему, но данные выводятся в 16-чном представлении.

Пример использования
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%include "io.inc"
 
section .data
        x       dd      123456789
        y       dd      -12345678
 
section .text
global CMAIN
CMAIN:
 
        PRINT_UDEC      4, x
        NEWLINE
        PRINT_DEC       4, y
        NEWLINE
        PRINT_HEX       4, y
 
        xor     eax,    eax
        ret


Использование библиотеки libc

Для вывода применяется функция форматного вывода _printf. Спецификаторы в форматной строке соответствуют аналогичным из языка C. С более полной спецификацией можно ознакомиться по ссылкам
https://learnc.info/c/formatted_input_output.html
https://ru.wikipedia.org/wiki/Printf
https://en.wikipedia.org/wiki/Printf_format_string

В стек помещаются значения чисел, адрес форматной строки и вызывается внешняя подпрограмма _printf, после вызова требуется самостоятельно очистить стек от передаваемых параметров.
Assembler
1
2
3
4
        push    dword [iNumber]         ;значение выводимого числа
        push    asFmtInt32              ;адрес форматной строки
        call    _printf                 ;вызов подпрограммы из библиотеки libc
        add     esp,    8               ;очистка стека от двух параметров размером 8 байт
Возможности вывода ограничиваются лишь возможностями библиотеки libc.
Пример использования из IDE SASM
компиляция командной строкой
Bash
1
2
nasm -g -f win32 $SOURCE$ -l $LSTOUTPUT$ -o $PROGRAM.OBJ$
gcc $PROGRAM.OBJ$ $MACRO.OBJ$ -g -o $PROGRAM$ -m32
Кликните здесь для просмотра всего текста
Assembler
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
%include "io.inc"
 
section .data
        iNumber         dd      -12345
        liNumber        dq      -123456789101112
        fsNumber        dd      123.456
        fdNumber        dq      -123.456
        feNumber        dt      123.456
 
        asFmtInt32      db      "int32:  %d", 0Ah, 0
        asFmtInt64      db      "int64:  %lld", 0Ah, 0
        asFmtSingle     db      "single: перед выводом необходимо привести к double", 0Ah, 0
        asFmtDouble     db      "double: %f", 0Ah, 0
 
section .text
global CMAIN
extern _printf
 
CMAIN:
 
        PRINT_STRING    "sasm: "
        PRINT_DEC       4, iNumber
        NEWLINE
 
        push    dword [iNumber]         ;значение выводимого числа
        push    asFmtInt32              ;адрес форматной строки
        call    _printf                 ;вызов подпрограммы из библиотеки libc
        add     esp,    8               ;очистка стека от двух параметров размером 8 байт
 
        push    dword [liNumber+4]      ;значение выводимого числа
        push    dword [liNumber]
        push    asFmtInt64              ;адрес форматной строки
        call    _printf                 ;вызов подпрограммы из библиотеки libc
        add     esp,    12              ;очистка стека от двух параметров размером 12 байт
 
        push    dword [fsNumber]
        push    asFmtSingle
        call    _printf                 ;вызов подпрограммы из библиотеки libc
        add     esp,    8
 
        push    dword [fdNumber+4]
        push    dword [fdNumber]
        push    asFmtDouble
        call    _printf                 ;вызов подпрограммы из библиотеки libc
        add     esp,    12
 
        xor     eax,    eax
        ret


Вывод средствами WinAPI wsprintf и WriteConsole (Windows)

Функция WinAPI wsprintf работает с форматной строкой, в которой указаны спецификаторы преобразования. Описание
https://docs.microsoft.com/en-... -wsprintfa
http://www.vsokovikov.narod.ru... printf.htm
Как видно из описания, преобразование выполняется для строк и целых чисел — вещественные числа (single, double, extended) этой функцией не преобразовываются.
Также, для вывода потребуется описатель вывода консоли — его можно получить перед использованием WriteConsole при помощи WinAPI функции GetStdHandle.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        x               dd      1234567890
        szFmtX          db      "X=%d", 0Dh, 0Ah, 0
        hConsoleOutput  resd    1
        buflen          resd    1
        buffer          resb    1024
..........................................................................................
        ;вывод одного числа в консоль
        ; - преобразование числа в строку
        push    dword [x]               ;значение выводимого числа
        push    szFmtX                  ;форматная строка
        push    buffer                  ;адрес буфера для формирования строки
        call    wsprintfA               ;вызов WinAPI функции для формирования строки
        add     esp,    12              ;освобождение стека от параметров
        mov     [buflen],       eax     ;сохранение длины строки в переменной
        ; - вывод строки в консоль
        push    0                       ;NULL
        push    buflen                  ;адрес переменной для каких то возвращаемых значений
        push    dword [buflen]          ;количество символов в выводимой строке
        push    buffer                  ;адрес строки
        push    dword [hConsoleOutput]  ;описатель вывода консоли
        call    WriteConsoleA           ;вызов функции WinAPI для вывода в консоль
Пример использования
компиляция командной строкой
Windows Batch file
1
2
nasm.exe -f obj main.asm -o main.obj
alink.exe -oPE -subsys console main.obj win32.lib
Библиотеку "win32.lib" нужно взять на официальном сайте компоновщика alink
https://sourceforge.net/projec... alink/1.6/
Кликните здесь для просмотра всего текста
Assembler
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
extern  GetStdHandle
extern  WriteConsoleA
extern  wsprintfA
extern  ExitProcess
 
%define STD_INPUT_HANDLE        -10
%define STD_OUTPUT_HANDLE       -11
%define STD_ERROR_HANDLE        -12
 
section .data   use32
        x               dd      1234567890
        y               dd      -987654321
 
        szFmtX          db      "X=%d", 0Dh, 0Ah, 0
        szFmtY          db      "Y=%d", 0Dh, 0Ah, 0
        szFmtXY         db      "X+Y=(%d)+(%d)=%d", 0Dh, 0Ah, 0
section         .bss    use32
        hConsoleInput   resd    1
        hConsoleOutput  resd    1
        buflen          resd    1
        buffer          resb    1024
section .text   use32
..start:                ;точка входа
        ; получение описателей ввода и вывода консоли
        push    STD_INPUT_HANDLE
        call    GetStdHandle
        mov     [hConsoleInput],        eax
        push    STD_OUTPUT_HANDLE
        call    GetStdHandle
        mov     [hConsoleOutput],       eax
        ;вывод одного числа в консоль
        ; - преобразование числа в строку
        push    dword [x]               ;значение выводимого числа
        push    szFmtX                  ;форматная строка
        push    buffer                  ;адрес буфера для формирования строки
        call    wsprintfA               ;вызов WinAPI функции для формирования строки
        add     esp,    12              ;освобождение стека от параметров
        mov     [buflen],       eax     ;сохранение длины строки в переменной
        ; - вывод строки в консоль
        call    ShowBufferString        ;вывод строки из буфера
        ;вывод одного числа в консоль
        push    dword [y]
        push    szFmtY
        push    buffer
        call    wsprintfA
        add     esp,    12
        mov     [buflen],       eax
        call    ShowBufferString
        ;вывод трёх чисел в консоль
        mov     eax,    [x]
        add     eax,    [y]
        push    eax
        push    dword [y]
        push    dword [x]
        push    szFmtXY
        push    buffer
        call    wsprintfA
        add     esp,    20
        mov     [buflen],       eax
        call    ShowBufferString
 
        push    0
        call    ExitProcess
 
;процедура вывода строки из буфера
ShowBufferString:
        push    0                       ;NULL
        push    buflen                  ;адрес переменной для каких то возвращаемых значений
        push    dword [buflen]          ;количество символов в выводимой строке
        push    buffer                  ;адрес строки
        push    dword [hConsoleOutput]  ;описатель вывода консоли
        call    WriteConsoleA           ;вызов функции WinAPI для вывода в консоль
ret


Вывод средствами msvcrt (Windows)

Библиотека msvcrt содержит функцию printf для вывода в консоль.
Функция позволяет выводить строки, целые числа (в восьмеричном, десятичном и шестнадцатеричном виде), вещественные числа двойной точности (double).
https://docs.microsoft.com/en-... ew=vs-2019
Для использования библиотеки msvcrt.dll необходимо объявить функцию printf внешней и указать источник импорта.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;Объявление внешних функций
extern  printf
extern  _getch
extern  ExitProcess
;Объявление источника импорта внешних функций
import printf   msvcrt.dll
import _getch   msvcrt.dll
.............................................................................................
        x               dd      1234567890
        y               dd      -987654321
 
        szFmtX          db      "X=%d", 0Dh, 0Ah, 0
        szFmtY          db      "Y=%d", 0Dh, 0Ah, 0
        szFmtXY         db      "X+Y=(%d)+(%d)=%d", 0Dh, 0Ah, 0
.............................................................................................
        ;вывод одного числа в консоль
        push    dword [x]               ;значение выводимого числа
        push    szFmtX                  ;форматная строка
        call    [printf]                ;вызов функции msvcrt для формирования и вывода строки
        add     esp,    8               ;освобождение стека от параметров
Пример использования
компиляция командной строкой
Windows Batch file
1
2
nasm.exe -f obj main.asm -o main.obj
alink.exe -oPE -subsys console main.obj win32.lib
Библиотеку "win32.lib" нужно взять на официальном сайте компоновщика alink
https://sourceforge.net/projec... alink/1.6/
Кликните здесь для просмотра всего текста
Assembler
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
;Объявление внешних функций
extern  printf
extern  _getch
extern  ExitProcess
;Объявление источника импорта внешних функций
import printf   msvcrt.dll
import _getch   msvcrt.dll
 
use32
section .data   use32
        x               dd      1234567890
        y               dd      -987654321
 
        szFmtX          db      "X=%d", 0Dh, 0Ah, 0
        szFmtY          db      "Y=%d", 0Dh, 0Ah, 0
        szFmtXY         db      "X+Y=(%d)+(%d)=%d", 0Dh, 0Ah, 0
section .text   use32
..start:                ;точка входа
        ;вывод одного числа в консоль
        push    dword [x]               ;значение выводимого числа
        push    szFmtX                  ;форматная строка
        call    [printf]                ;вызов функции msvcrt для формирования и вывода строки
        add     esp,    8               ;освобождение стека от параметров
        ;вывод одного числа в консоль
        push    dword [y]
        push    szFmtY
        call    [printf]
        add     esp,    8
        ;вывод трёх чисел в консоль
        mov     eax,    [x]
        add     eax,    [y]
        push    eax
        push    dword [y]
        push    dword [x]
        push    szFmtXY
        call    [printf]
        add     esp,    16
 
        call    [_getch]
 
        push    0
        call    ExitProcess


И то же самое, но с применением пакета nasmx (https://sourceforge.net/projects/nasmx/)
Взял код Jin X из сообщения [NASM] Вывод на экран
компиляция командной строкой (в оригинале Jin X предлагает чуть более сложный пакетный файл)
Windows Batch file
1
2
nasm -f win32 main.asm -o main.obj
golink /console /mix main.obj kernel32.dll msvcrt.dll
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
%include 'nasmx.inc'
import printf
import ExitProcess, 4
 
[section .text]
 
proc    start
locals  none
        mov     eax,3
        add     eax,2
        invoke  printf, Fmt, eax
        invoke  ExitProcess, 0
endproc
 
[section .data]
 
Fmt     db      `%i\0`
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
04.10.2020, 17:35

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Сложение чисел в различных системах счисления
1) Сложить эти два числа в указанных СС в скобках... 2) Перевести ответ в 16ую СС...

Запись действительных десятичных чисел в различных системах счисления
Дали задание: &quot;Запись действительных десятичных чисел в различных системах счисления&quot; Нарисовать...

Программа для преобразования чисел в различных системах счисления
Помогите с кодом пожалуйста. Буду благодарен! Реализовать ввод и перевод чисел с троичной в...

Сгенерировать калькулятор целых и вещественных чисел в различных системах счисления
сгенерировать в C# калькулятор целых и вещественных чисел в различных системах счисления

Программа для расчета выражения. Ввод/вывод в разных системах счисления
Разработать алгоритм и программу расчета выражения в соответствии с индивидуальным заданием на...

Калькулятор в различных системах счисления
Помогите написать программу, которая будет проводить арифметические операции в различных системах...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.