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

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

06.10.2009, 14:14. Просмотров 96779. Ответов 7

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

люди помогите пожалуйста
8
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.10.2009, 14:14
Ответы с готовыми решениями:

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

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

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

Ввод символа и вывод его ASCII-кода в заданной системе счисления
Здравствуйте у меня возникла проблема мне нужно реализовать программу которая...

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

7
Goodwin98
2521 / 817 / 10
Регистрация: 31.05.2009
Сообщений: 1,672
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
70
vital792
1998 / 1270 / 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
43
ФедосеевПавел
Модератор
3598 / 1982 / 825
Регистрация: 01.02.2015
Сообщений: 6,599
05.01.2017, 19:29 #4
Добавлю и "свои" способы ввода и вывода.

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

Даже не буду обманывать - исходник когда-то взял на форуме, лишь немного доработал под требования решаемой задачи и переименовал метки, т.к. в те далёкие времена ещё наивно пояснял решения. Но название процедуры осталось неизменным.

Вывод целого беззнакового числа
Подпрограмма в основном повторяет аналогичный код Goodwin98, отличие лишь в именовании меток и сохранении регистров при вызове подпрограммы. При желании разобраться в алгоритме - можно по-строчно сравнивать с кодом 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
; выводит число из регистра AX на экран
; входные данные:
; ax - число для отображения
; cx - система счисления (не больше 10)
Show_AX proc
        push    ax
        push    bx
        push    cx
        push    dx
        push    di
 
        ;mov     cx, 10          ; cx - основание системы счисления
        xor     di, di          ; di - кол. цифр в числе
 
@@Conv:
        xor     dx, dx
        div     cx              ; dl = num mod 10
        add     dl, '0'         ; перевод в символьный формат
        inc     di
        push    dx              ; складываем в стэк
        or      ax, ax
        jnz     @@Conv
        ; выводим из стэка на экран
@@Show:
        pop     dx              ; dl = очередной символ
        mov     ah, 2           ; ah - функция вывода символа на экран
        int     21h
        dec     di              ; повторяем пока di<>0
        jnz     @@Show
 
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Show_AX endp
Применение
Assembler
        mov     ax, 12345 ;или иначе, поместив отображаемое число в ax
        mov     cx, 10    ;основание системы счисления выводимого числа
        call    Show_AX
        mov     cx, 8     ;основание системы счисления выводимого числа
        call    Show_AX
Естественно, что можно расскоментировать строку 12 и тогда вывод будет исключительно в 10 системе счисления, в этом случае отпадёт необходимость загружать в cx основание системы счисления перед каждым вызовом.

Вывод целого знакового числа
Как справедливо отметил в комментариях 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
40
41
42
43
44
45
46
47
48
49
; выводит число из регистра AX на экран
; входные данные:
; ax - число для отображения
Show_AX proc
        push    ax
        push    bx
        push    cx
        push    dx
        push    di
 
        mov     cx, 10          ; cx - основание системы счисления
        xor     di, di          ; di - кол. цифр в числе
 
        ; если число в ax отрицательное, то
        ;1) напечатать '-'
        ;2) сделать ax положительным
        or      ax, ax
        jns     @@Conv
        push    ax
        mov     dx, '-'
        mov     ah, 2           ; ah - функция вывода символа на экран
        int     21h
        pop     ax
 
        neg     ax
 
@@Conv:
        xor     dx, dx
        div     cx              ; dl = num mod 10
        add     dl, '0'         ; перевод в символьный формат
        inc     di
        push    dx              ; складываем в стэк
        or      ax, ax
        jnz     @@Conv
        ; выводим из стэка на экран
@@Show:
        pop     dx              ; dl = очередной символ
        mov     ah, 2           ; ah - функция вывода символа на экран
        int     21h
        dec     di              ; повторяем пока di<>0
        jnz     @@Show
 
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Show_AX endp
Здесь основание системы счисления прописано в теле процедуры и равно 10, но при необходимости можно закомментировать строку 11 и на вход процедуре подавать основание системы счисления в регистре cx.
Применение
Assembler
        mov     ax, -12345 ;или иначе, поместив отображаемое число в ax
        call    Show_AX
Вывод шестнадцатеричного числа
При выводе по основанию системы счисления являющемуся степенью 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.

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

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

Вывод двоичного числа
Приведённый код почти соответствует примеру 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
Вывод восьмеричного числа
Как видно, эта процедура почти полная копия варианта 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
proc3nt
волшебник
644 / 506 / 151
Регистрация: 26.05.2012
Сообщений: 1,429
05.01.2017, 21:20 #5
мой "подарок" форуму, перевод числа из 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
ФедосеевПавел
Модератор
3598 / 1982 / 825
Регистрация: 01.02.2015
Сообщений: 6,599
05.01.2017, 22:33 #6
Ввод чисел несколько сложнее вывода по причине возможности ошибочности входных данных (в строке присутствуют не только цифры или число в строке превышает разрядность выделяемой для него ячейки памяти).

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

Ввод беззнакового числа в почти произвольной (не более 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
Применение.
Пример применения получился сложнее, т.к. требовалось показать обработку ошибок преобразования строки в число в самой программе. Поэтому здесь приведу ссылку на пример из именно этих процедур.
http://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


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

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

Имеет смысл в закреплённой теме привести также ссылку на соответствующую главу http://www.cyberforum.ru/post6454877.html с несколькими примерами взаимных преобразований строк в числа. Они, правда, для разрядности x32 и выше, но могут также пригодится при невозможности использования функций API Windows или Linux.
1
Jin X
Заблокирован
04.02.2017, 21:01 #7
Преобразование 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 Кб, 5 просмотров)
ФедосеевПавел
Модератор
3598 / 1982 / 825
Регистрация: 01.02.2015
Сообщений: 6,599
01.07.2017, 22:07 #8
Вывод целого беззнакового 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
Просмотров: 1127

Размер: 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
http://www.cyberforum.ru/post6454877.html
0
01.07.2017, 22:07
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
01.07.2017, 22:07

Числа в разных системах счисления
Задана строка, представляющая собой запись числа. Определить в какой системе...

Десятичная система счисления, вывод для чисел больше 100
.model tiny .data .code .186 org 100h start: mov ax,100 call print ret

Ввод и вывод чисел
Как ввести число 2017 ?; как вывести число 137?(если учесть, что необходимо...


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

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

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