5.2.4. Ввод и вывод чисел
При работе с числами на ассемблере перед каждым программистом встает вопрос, как осуществлять ввод и вывод чисел в программе? Действительно, ввод осуществляется в ASCII-кодах, а вычисления нужно производить с обычными числами; результат вычислений на экран вновь нужно выводить в ASCII-кодах.
Таким образом, при работе с числами в программе на ассемблере необходимы две процедуры: первая должна преобразовывать введенные пользователем числа из ASCII-формата в бинарные числа, а вторая процедура должна наоборот преобразовывать результат вычислений в ASCII-коды для вывода на экран. К сожалению, стандартных команд, которое бы это делали, не существует. В листинге 5.1 вы можете видеть программу, которая запрашивает два числа, складывает их и выводит результат на экран.
В этой программе процедура str2int преобразовывает каждое число из буфера в ASCII-формате в соответствующие бинарное число.
Другая процедура int2str преобразовывает бинарное число (результат вычисления) в число в системе счисления base в ASCII-формате.
Обращаю ваше внимание, что авторство на данные процедуры принадлежит не мне (если авторы объявятся, я укажу их в переиздании этой книги).
К настоящему времени весь код программы вы должны в состоянии понять самостоятельно, а комментарии вам в этом помогут.
How to print a number in assembly NASM?
Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as «hello, world».
I’m developing on Linux.
6 Answers 6
If you’re already on Linux, there’s no need to do the conversion yourself. Just use printf instead:
Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
You have to convert it in a string; if you’re talking about hex numbers it’s pretty easy. Any number can be represented this way:
So when you have this number you have to split it like I’ve shown then convert every «section» to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we’re interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here’s what I mean (soppose we want to take the 3):
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0’s ASCII value (0x30), if it’s greater than 9 we have to use a’s ASCII value (0x61).
Here it is, now we just have to code it:
PS: I know this is 16 bit code but I still use the old TASM 😛
PPS: this is Intel syntax, converting to AT&T syntax isn’t difficult though, look here.
FasmWorld Программирование на ассемблере FASM для начинающих и не только
Учебный курс. Часть 22. Вывод чисел на консоль
Автор: xrnd | Рубрика: Исходники, Учебный курс | 31-07-2010 | Распечатать запись
В качестве примера программирования процедур займёмся такой важной проблемой, как вывод на консоль чисел в различных системах счисления. Проблема эта возникает потому, что в ассемблере нет никаких специальных средств для вывода чисел, а с помощью стандартных функций можно выводить только строки.
Следовательно, задача сводится к тому, чтобы преобразовать двоичное число в строку символов, а затем вывести эту строку на экран. Все процедуры в этой части являются лишь примерами, вы можете использовать их или написать свои собственные процедуры, более удобные для вас.
Для начала рассмотрим две полезные процедуры, которые будут использоваться в дальнейшем. Чтобы постоянно не обращаться в коде в функции DOS 09h, удобно написать маленькую процедуру для вывода строки:
;Процедура вывода строки на консоль ; DI — адрес строки print_str: push ax mov ah,9 ;Функция DOS 09h — вывод строки xchg dx,di ;Обмен значениями DX и DI int 21h ;Обращение к функции DOS xchg dx,di ;Обмен значениями DX и DI pop ax ret
В качестве параметра ей передаётся адрес строки в регистре DI. Строка должна оканчиваться символом ‘$’. Здесь используется команда XCHG, которая выполняет обмен значениями двух операндов.
Вторая полезная процедура — вывод конца строки. Она вызывает первую процедуру для вывода двух символов CR(13) и LF(10). Вызывается без параметров и не изменяет регистры.
;Процедура вывода конца строки (CR+LF) print_endline: push di mov di,endline ;DI = адрес строки с символами CR,LF call print_str ;Вывод строки на консоль pop di ret . endline db 13,10,’$’
Вывод чисел в двоичном виде
Алгоритм вывода в двоичном виде очень прост. Нужно проанализировать все биты числа и поместить в строку символы ‘0’ или ‘1’ в зависимости от значения соответствующего бита. Удобно делать это с помощью циклического сдвига в цикле. Сдвинутый бит оказывается в флаге CF, а после завершения цикла в регистре то же значение, что и в начале. Процедура byte_to_bin_str преобразует байт в регистре AL в строку. Адрес буфера для строки передаётся в регистре DI. Процедура всегда записывает в буфер 8 символов, так как в байте 8 бит.
;Процедура преобразования байта в строку в двоичном виде ; AL — байт. ; DI — буфер для строки (8 символов). Значение регистра не сохраняется. byte_to_bin_str: push cx ;Сохранение CX mov cx,8 ;Счётчик цикла btbs_lp: rol al,1 ;Циклический сдвиг AL влево на 1 бит jc btbs_1 ;Если выдвинутый бит = 1, то переход mov byte[di],’0′ ;Добавление символа ‘0’ в строку jmp btbs_end btbs_1: mov byte[di],’1′ ;Добавление символа ‘1’ в строку btbs_end: inc di ;Инкремент DI loop btbs_lp ;Команда цикла pop cx ;Восстановление CX ret ;Возврат из процедуры
Используя эту процедуру, легко написать ещё одну для вывода слова в двоичном виде:
;Процедура преобразования слова в строку в двоичном виде ; AX — слово ; DI — буфер для строки (16 символов). Значение регистра не сохраняется. word_to_bin_str: xchg ah,al ;Обмен AH и AL call byte_to_bin_str ;Преобразование старшего байта в строку xchg ah,al ;Обмен AH и AL call byte_to_bin_str ;Преобразование младшего байта в строку ret
И наконец вот две процедуры, которые делают то, что нужно Буфер имеет размер 17 символов, так как в слове 16 бит + символ ‘$’, обозначающий конец строки.
;Процедура вывода байта на консоль в двоичном виде ; AL — байт print_byte_bin: push di mov di,buffer ;DI = адрес буфера call byte_to_bin_str ;Преобразование байта в AL в строку mov byte[di],’$’ ;Добавление символа конца строки sub di,8 ;DI = адрес начала строки call print_str ;Вывод строки на консоль pop di ret ;Процедура вывода слова на консоль в двоичном виде ; AX — слово print_word_bin: push di mov di,buffer ;DI = адрес буфера call word_to_bin_str ;Преобразование слова в AX в строку mov byte[di],’$’ ;Добавление символа конца строки sub di,16 ;DI = адрес начала строки call print_str ;Вывод строки на консоль pop di ret . buffer rb 17
Полный исходный код примера вы можете скачать отсюда: printbin.asm. Результат работы программы выглядит вот так:

Вывод чисел в шестнадцатеричном виде
Этот пример по структуре похож на предыдущий, поэтому для краткости я рассмотрю только сами процедуры преобразования числа в строку. Преобразование в шестнадцатеричный вид удобно выполнять группами по 4 бита, то есть по тетрадам. Каждая тетрада будет представлять собой одну шестнадцатеричную цифру. Я написал отдельную процедуру для преобразования тетрады в символ цифры:
;Процедура преобразования числа (0-15) в шестнадцатеричную цифру ; вход : AL — число (0-15) ; выход: AL — шестнадцатеричная цифра (‘0’-‘F’) to_hex_digit: add al,’0′ ;Прибавляем символ ‘0’ (код 0x30) cmp al,’9′ ;Сравнение с символом ‘9’ (код 0x39) jle thd_end ;Если получилось ‘0’-‘9’, то выход add al,7 ;Прибавляем ещё 7 для символов ‘A’-‘F’ thd_end: ret
Если значение тетрады от 0 до 9, то достаточно только прибавить код символа ‘0’ (0x30). А если значение больше 9, то надо прибавить ещё 7, чтобы получилась буква ‘A’-‘F’.
Теперь легко можно преобразовать байт в шестнадцатеричную строку, достаточно каждую из его тетрад заменить соответствующей цифрой:
;Процедура преобразования байта в строку в шестнадцатеричном виде ; AL — байт. ; DI — буфер для строки (2 символа). Значение регистра не сохраняется. byte_to_hex_str: push ax mov ah,al ;Сохранение значения AL в AH shr al,4 ;Выделение старшей тетрады call to_hex_digit ;Преобразование в шестнадцатеричную цифру mov [di],al ;Добавление символа в строку inc di ;Инкремент DI mov al,ah ;Восстановление AL and al,0Fh ;Выделение младшей тетрады call to_hex_digit ;Преобразование в шестнадцатеричную цифру mov [di],al ;Добавление символа в строку inc di ;Инкремент DI pop ax ret
Преобразование слова также не представляет трудности — сначала преобразуем старший байт, затем младший:
;Процедура преобразования слова в строку в шестнадцатеричном виде ; AX — слово ; DI — буфер для строки (4 символа). Значение регистра не сохраняется. word_to_hex_str: xchg ah,al ;Обмен AH и AL call byte_to_hex_str ;Преобразование старшего байта в строку xchg ah,al ;Обмен AH и AL call byte_to_hex_str ;Преобразование младшего байта в строку ret
Полный исходный код примера: printhex.asm. Результат работы программы выглядит вот так:

Вывод чисел в десятичном виде
С десятичными числами немного сложнее. Для начала займёмся числами без знака. Чтобы преобразовать число в десятичную строку необходимо в цикле делить его на 10 (это основание системы счисления). Остатки от деления дают нам значения десятичных цифр. Первый остаток — младшая цифра, последний — старшая. Деление продолжается пока частное не равно нулю.
Например, если есть число 125. Делим его на десять: получаем 12, 5 в остатке. Потом делим 12 на десять: получаем 1, 2 в остатке. Наконец, 1 делим на 10: получаем 0, 1 в остатке. Цифры числа, начиная с младшей: 5, 2, 1. Так как обычно десятичные числа пишут, начиная со старшей цифры, то необходимо переставить их наоборот Я для этого использовал стек.
В первом цикле производится деление, полученные остатки преобразуются в цифры и помещаются в стек. Во втором цикле символы извлекаются из стека (в обратном порядке) и помещаются в строку. Так как максимальное значение слова без знака 65536 (5 цифр), то в буфер записывается максимум 5 символов.
;Процедура преобразования слова в строку в десятичном виде (без знака) ; AX — слово ; DI — буфер для строки (5 символов). Значение регистра не сохраняется. word_to_udec_str: push ax push cx push dx push bx xor cx,cx ;Обнуление CX mov bx,10 ;В BX делитель (10 для десятичной системы) wtuds_lp1: ;Цикл получения остатков от деления xor dx,dx ;Обнуление старшей части двойного слова div bx ;Деление AX=(DX:AX)/BX, остаток в DX add dl,’0′ ;Преобразование остатка в код символа push dx ;Сохранение в стеке inc cx ;Увеличение счетчика символов test ax,ax ;Проверка AX jnz wtuds_lp1 ;Переход к началу цикла, если частное не 0. wtuds_lp2: ;Цикл извлечения символов из стека pop dx ;Восстановление символа из стека mov [di],dl ;Сохранение символа в буфере inc di ;Инкремент адреса буфера loop wtuds_lp2 ;Команда цикла pop bx pop dx pop cx pop ax ret
Для вывода байта можно преобразовать его в слово и воспользоваться той же процедурой:
;Процедура преобразования байта в строку в десятичном виде (без знака) ; AL — байт. ; DI — буфер для строки (3 символа). Значение регистра не сохраняется. byte_to_udec_str: push ax xor ah,ah ;Преобразование байта в слово (без знака) call word_to_udec_str ;Вызов процедуры для слова без знака pop ax ret
Теперь разберёмся с числами со знаком. Сначала нужно проверить старший бит числа. Если число положительное, то его можно преобразовать также как число без знака. Если число отрицательное, то добавляем в строку символ ‘-‘, а затем инвертируем число и преобразуем как беззнаковое.
;Процедура преобразования слова в строку в десятичном виде (со знаком) ; AX — слово ; DI — буфер для строки (6 символов). Значение регистра не сохраняется. word_to_sdec_str: push ax test ax,ax ;Проверка знака AX jns wtsds_no_sign ;Если >= 0, преобразуем как беззнаковое mov byte[di],’-‘ ;Добавление знака в начало строки inc di ;Инкремент DI neg ax ;Изменение знака значения AX wtsds_no_sign: call word_to_udec_str ;Преобразование беззнакового значения pop ax ret
;Процедура преобразования байта в строку в десятичном виде (со знаком) ; AL — байт. ; DI — буфер для строки (4 символа). Значение регистра не сохраняется. byte_to_sdec_str: push ax movsx ax,al ;Преобразование байта в слово (со знаком) call word_to_sdec_str ;Вызов процедуры для слова со знаком pop ax ret
Полный исходный код примера: printdec.asm. Результат работы программы выглядит вот так:

Как вывести на консоль в десятичном виде очень большое число (> 32 бит) читайте здесь.
Вывод чисел в восьмеричном виде
Выводить числа в восьмеричном виде приходится достаточно редко, поэтому подробно описывать не буду. Можно либо делить число последовательно на 8, либо преобразовывать в цифры группы по 3 бита. Я использовал второй вариант. Смотрите код примера: printoct.asm. Результат работы программы:

Вывод чисел в других системах счисления
Реализуется также, как вывод в десятичном виде — с помощью алгоритма последовательного деления на основание системы счисления. Например, если вам нужно вывести число в пятеричной системе счисления, делить надо на 5, а не на 10.
Упражнение
Напишите программу для вывода на консоль массива слов со знаком в десятичном виде (например, через запятую). Для вывода чисел можете воспользоваться моим примером или написать свою собственную процедуру. Результаты можете писать в комментариях.
20. Вывод чисел на консоль в ассемблере i8086
Статья основана на материале xrnd с сайта asmworld (из учебного курса по программированию на ассемблер 16-битного процессора 8086 под DOS).
В качестве примера программирования процедур займёмся такой важной проблемой, как вывод на консоль чисел в различных системах счисления. Проблема эта возникает потому, что в ассемблере нет никаких специальных средств для вывода чисел, а с помощью стандартных функций можно выводить только строки.
Следовательно, задача сводится к тому, чтобы преобразовать двоичное число в строку символов, а затем вывести эту строку на экран. Все процедуры в этой части являются лишь примерами, вы можете использовать их или написать свои собственные процедуры, более удобные для вас.
Для начала рассмотрим две полезные процедуры, которые будут использоваться в дальнейшем. Чтобы постоянно не обращаться в коде в функции DOS 09h, удобно написать маленькую процедуру для вывода строки:
В качестве параметра ей передаётся адрес строки в регистре DI. Строка должна оканчиваться символом ‘$’. Здесь используется команда XCHG , которая выполняет обмен значениями двух операндов.
Вторая полезная процедура — вывод конца строки. Она вызывает первую процедуру для вывода двух символов CR(13) и LF(10). Вызывается без параметров и не изменяет регистры.
Вывод чисел в двоичном виде
Алгоритм вывода в двоичном виде очень прост. Нужно проанализировать все биты числа и поместить в строку символы ‘0’ или ‘1’ в зависимости от значения соответствующего бита. Удобно делать это с помощью циклического сдвига в цикле. Сдвинутый бит оказывается в флаге CF, а после завершения цикла в регистре то же значение, что и в начале. Процедура byte_to_bin_str преобразует байт в регистре AL в строку. Адрес буфера для строки передаётся в регистре DI. Процедура всегда записывает в буфер 8 символов, так как в байте 8 бит.
Используя эту процедуру, легко написать ещё одну для вывода слова в двоичном виде:
И наконец вот две процедуры, которые делают то, что нужно. Буфер имеет размер 17 символов, так как в слове 16 бит + символ ‘$’ , обозначающий конец строки.
Результат работы программы выглядит вот так: 
Вывод чисел в шестнадцатеричном виде
Этот пример по структуре похож на предыдущий, поэтому для краткости я рассмотрю только сами процедуры преобразования числа в строку. Преобразование в шестнадцатеричный вид удобно выполнять группами по 4 бита, то есть по тетрадам. Каждая тетрада будет представлять собой одну шестнадцатеричную цифру. Я написал отдельную процедуру для преобразования тетрады в символ цифры:
Если значение тетрады от 0 до 9, то достаточно только прибавить код символа ‘0’ (0x30). А если значение больше 9, то надо прибавить ещё 7, чтобы получилась буква ‘A’-‘F’.
Теперь легко можно преобразовать байт в шестнадцатеричную строку, достаточно каждую из его тетрад заменить соответствующей цифрой:
Преобразование слова также не представляет трудности — сначала преобразуем старший байт, затем младший:
Результат работы программы выглядит вот так: 
Вывод чисел в десятичном виде
С десятичными числами немного сложнее. Для начала займёмся числами без знака. Чтобы преобразовать число в десятичную строку необходимо в цикле делить его на 10 (это основание системы счисления). Остатки от деления дают нам значения десятичных цифр. Первый остаток — младшая цифра, последний — старшая. Деление продолжается пока частное не равно нулю.
Например, если есть число 125. Делим его на десять: получаем 12, 5 в остатке. Потом делим 12 на десять: получаем 1, 2 в остатке. Наконец, 1 делим на 10: получаем 0, 1 в остатке. Цифры числа, начиная с младшей: 5, 2, 1. Так как обычно десятичные числа пишут, начиная со старшей цифры, то необходимо переставить их наоборот. Я для этого использовал стек.
В первом цикле производится деление, полученные остатки преобразуются в цифры и помещаются в стек. Во втором цикле символы извлекаются из стека (в обратном порядке) и помещаются в строку. Так как максимальное значение слова без знака 65536 (5 цифр), то в буфер записывается максимум 5 символов.
Для вывода байта можно преобразовать его в слово и воспользоваться той же процедурой:
Теперь разберёмся с числами со знаком. Сначала нужно проверить старший бит числа. Если число положительное, то его можно преобразовать также как число без знака. Если число отрицательное, то добавляем в строку символ ‘-‘, а затем инвертируем число и преобразуем как беззнаковое.
Результат работы программы выглядит вот так:

Как вывести на консоль в десятичном виде очень большое число (> 32 бит) читайте здесь.
Вывод чисел в восьмеричном виде
Выводить числа в восьмеричном виде приходится достаточно редко, поэтому подробно описывать не буду. Можно либо делить число последовательно на 8, либо преобразовывать в цифры группы по 3 бита. Я использовал второй вариант.
Результат работы программы:

Вывод чисел в других системах счисления
Реализуется также, как вывод в десятичном виде — с помощью алгоритма последовательного деления на основание системы счисления. Например, если вам нужно вывести число в пятеричной системе счисления, делить надо на 5, а не на 10.
Упражнение
Напишите программу для вывода на консоль массива слов со знаком в десятичном виде (например, через запятую). Для вывода чисел можете воспользоваться моим примером или написать свою собственную процедуру. Результаты можете писать в комментариях или на форуме.