Assembler вывод числа на экран — ПК портал
Содержание
- Функции вывода DOS
- Функции вывода BIOS
- Вспомним о процедурах
- Вывод на экран
- Запуск программы
- ЗАДАЧА 1
- РЕШЕНИЕ
- ЗАДАЧА 2
- РЕШЕНИЕ
| Как выучить английский |
В наше время любой человек должен знать английский язык. А тем более программист. Потому что довольно большая часть документации существует только на английском. А профессионал не может ждать, пока кто-то переведёт ему документацию. Он должен уметь быстро получать нужную инфорамцию и использовать её, независимо от того, на каком она языке — на английском или на русском. Ссылки на курсы по английскому.
Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.
Правда, в современных воплощениях языка Ассемблера могут быть стандартные макросы или подпрограммы для вывода строк на экран. Однако начинающим будет полезно разобраться с тем, как это можно сделать только с помощью инструкций, без применения каких-либо облегчающих жизнь библиотек.
Итак, выводить строки на экран можно двумя путями:
- Посимвольно (то есть в цикле выводить каждый символ строки).
- Строку целиком.
Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:
- С помощью функций DOS.
- С помощью функций BIOS.
- Путём прямой записи в видеопамять.
Третий способ хорош тем, что он сразу записывает данные в видеопамять, что позволяет выполнять вывод более быстро. Однако в наше время он применим, разве что, в учебных целях. Потому что современные операционные системы не позволяют напрямую обращаться к “железу”.
Поэтому, если ваша программа является чуть более, чем учебной, то придётся применять один из первых двух способов. Ну а если вам интересно, как выполнить вывод непосредственно в видеопамять, то посмотреть примеры вы можете здесь или здесь.
Функции вывода DOS
Итак, начнём с функций вывода DOS. Эти функции являются функциями операционной системы DOS, но поддерживаются и операционными системами Windows.
Как я уже говорил, можно напечатать на экране строку в цикле, отдельно выводя каждый символ. Для этих целей можно использовать функции 02h, 06h или недокуметированное прерывание 29h.
Если требуется вывести на экран строку целиком, то можно применить функции 09h и 40h.
Для использования функций DOS надо сначала подготовить необходимые данные, записать номер функции в регистр AH, а затем вызвать прерывание 21h.
Все функции рассматривать здесь не будем. Для примера используем одну из самых простых функций DOS — функцию 09h, которая выводит в стандартное устройство вывода (в нашем случае — на экран) строку, адрес которой находится в DS:DX.
Строка должна заканчиваться символом $, иначе функция не поймёт, где конец строки, и программа будет выводить много-много символов из памяти, начиная с указанного адреса. Примерно так, как показано на рисунке:
При правильном использовании на экран будет выведено примерно следующее:
Функции вывода BIOS
Функции BIOS также могут выводить как отдельные символы (функции 09h, 0Ah, 0Eh), так и строки целиком (функция 13h).
Кроме того с помощью функций BIOS можно установить видеорежим, установить или считать положение курсора, а также считать символ и его атрибуты.
Хотя функции DOS тоже могут считывать символы, но всё-таки возможности BIOS более широки.
Для работы с функциями BIOS также сначала надо подготовить данные, записать номер функции в регистр AH, а затем вызвать прерывание 10h.
Для примера рассмотрим функцию 13h. Перед вызовом функции надо:
- Записать номер функции в регистр АН.
- Записать режим вывода в регистр AL:
- бит 0: переместить курсор в конец строки после вывода.

- бит 1: строка содержит не только символы, но и атрибуты. Так что каждый символ описывается двумя байтами — ASCII-код и атрибут. Это можно использовать, если в строке символы должны иметь, например, разный цвет. Если атрибуты одинаковы для всей строки, то этот бит лучше сбросить (обнулить).
- биты 2-7: не используются.
Рассказывать об остальных функциях, а также об установке атрибутов и прочих вещах сегодня не буду. Если кому интересно, то всё это можно найти в справочных материалах.
Сегодня мы рассмотрим тему «Вывод на экран Assembler» и разберем программу, которая сможет выводить на экран нашей консоли числа.
Для этого нам потребуется изучить несколько новых процедур, а также познакомится с подключаемыми библиотеками и системными файлами.
Вспомним о процедурах
Для начала, я напомню вам, что когда мы изучали с вами процедуры Assembler, то я, подводя итоги, написал, что для процедур с параметрами необходим прототип. Да, это так, но есть одно но: если эта процедура не является стандартной, и не описана где нибудь в подключаемой библиотеки.
Так вот, ранее изученная процедура ExitProcess, а также те процедуры, которые мы сегодня с вами изучим(WriteConsoleA, wsprintf) являются стандартными(на самом деле такого термина нет, но думаю так понятнее), и для них не нужно писать прототип.
Вывод на экран
А теперь перейдем к коду:
Как вы заметили, у нас увеличилось число подключаемых файлов. И как я уже выше написал, все они понадобятся для того, чтобы работали процедуры, которые мы будем использовать далее в этой программе.
Далее будет не очень понятно пока не увидим в действии, но все же попробую объяснить.
Первое, что мы делаем, объявляем константу BSIZE со значением 15. Затем в разделе .data , там где объявляются переменные, задаем несколько переменных, которые потребуются как параметры для наших дальнейших процедур.
Стоит отметить переменную buf: запись dup(duplication) говорит о подряд идущих байт, которые выделяются под переменную buf размерностью BSIZE(15). А знак вопроса говорит, что значения байт заранее не определено.
Итак, пока не все понятно, поэтому идем дальше, тут у вас должен сложиться пазл:
Для того, чтобы вывести что нибудь на экран нужно знать дескриптор вывода. Процедура GetStdHandle считывает адрес дескриптора экрана, и по умолчанию записывает его в регистр eax. После, мы записываем это значение из регистра в переменную stdout.
Дальше мы используем процедуру wsprintf. Она нужна для того, чтобы вывести наше сообщение(именно число) в понятном для нас формате, то есть эта процедура преобразования машинного языка в человеческий формат.
Для этого используется переменная ifmt.
Ну и за сам вывод отвечает процедура WriteConsoleA. Она принимает 5 параметров, и последним всегда должен идти 0.
Мой совет: лучше запомнить эти процедуры в таком виде, каком они представлены здесь, в наших дальнейших статьях мы практически всегда будем их использовать.
Запуск программы
А сейчас я покажу вам как все это реализовать на практике, после написания кода.
Я создал файл fifth.asm и поместил его в папку BIN. Затем я открываю командную строку, и перехожу в папку BIN с помощью команды cd C:UsersНикитаBIN (у вас будет другой путь, скорее всего)
Далее компилирую наш файл, прописывая команду amake.bat fifth, если код правильный, то будет как на фото:
Как мы видим, все скомпилировалось без ошибок, но ничего не вывелось на экран. Для того, чтобы наша программа выполнилась, нужно после компиляции запустить файл с расширением .exe(он создается автоматически после правильной компиляции).
Прописываем в командной строке fifth.exe, у вас должно получится нечто подобное:
Все прошло успешно! На сегодня на этом закончим c выводом на экран, но в следующих примерах Assembler мы еще будем возвращаться к этой теме, если у вас остались вопросы, то пишите их в комментариях, не забывайте просматривать исходники и предыдущие статьи.
Методические указания к выполнению лабораторной работы
1) ознакомиться с теоретическим материалом;
2) разобрать реализацию приведенных задач;
3) дописать необходимые команды, создать исполняемые файлы и выполнить программы, реализующие решение задач 1, 2.
4) проверить правильность работы программ на тестах.
Для самопроверки и для контроля преподавателем необходимо выполнить все задачи.
Анализ таблицы кодов ASCII показывает следующее.
В системе кодировки ASCII для любой цифры от 0 до 9 справедливо соотношение
код (цифра) — код (‘0’) = цифра
Так как код символа 0 (‘0’) равен 30 h , то ASCII -код любой цифры от 0 до 9 отличается от соответствующего двоичного представления числа на 30 h .
Поэтому для преобразования ASCII -кода символа (‘0’..’9’) в число, следует из кода символа вычесть 30 h .
При вводе с клавиатуры, например, цифры 5 недостаточно воспользоваться функцией 1 h прерывания 21 h , так как в регистре al в результате будет находиться код символа ’5’, а не число 5.
Для получения числа 5 необходимо еще вычесть 30 h из полученного кода:
mov 21 h ; в al код символа ‘5’
sub al ,30 h ; теперь в al число 5
ЗАДАЧА 1
Пусть в сегменте данных под символическим именем N хранится беззнаковое десятичное число (от 0 до 255). Необходимо записать по адресу KOD цифры (как символы) из десятичной записи числа.
РЕШЕНИЕ
Пусть N = abc , где a , b , c — десятичные цифры числа N .
Для получения правой цифры c надо взять остаток от деления N на 10. Неполное частное от деления — это число ab , ели его разделить на 10, то неполное частное даст цифру а, а остаток — цифру b .
Чтобы получить эти цифры как символы (их затем можно будет вывести на экран), следует к цифре прибавить код ‘0’ (30 h ).
mov ah ,0 ; расширение N в ax до слова (беззнаковое)
div bl ; ah=c, al=ab
mov KOD +2, ah ; записали последнюю цифру
mov ah ,0 ; al = ab , расширение ab до слова (беззнаковое)
div bl ; ah=b, al=a
add ax,’00’ ; ah=b+’0’, al=a+’0’
mov KOD +1, ah ; записали среднюю цифру
mov KOD , al ; записали первую цифру
ЗАДАЧА 2
Ввести десятичное число и записать его в сегмент данных под символическим именем N (размером в один байт).
РЕШЕНИЕ
По очереди вводим цифры и формируем число по схеме Горнера.
Пусть уже введены первые цифры числа, например, 2 и 1, и по ним сформировано число 21. Пусть следующей введена цифра 3. Тогда умножаем предыдущее число на 10 и прибавляем к нему новую цифру: 21*10+3=213. И так для каждой новой цифры.
Замечание. Будем предполагать, что пользователь вводит число в диапазоне 0-255 корректно и ввод завершается нажатием клавиши Enter .
int 21 h ; в al — первый символ
sub al ,30 h ; теперь первая цифра
mov ah ,0 ; расширение до слова
mov cx , ax ; в cx — первая цифра
int 21 h ; в al следующий символ
cmp al ,0 dh ; сравнение с символом Enter
je End ; конец ввода
sub al ,30 h ; в al — следующая цифра
cbw ; расширение до слова
xchg ax , cx ; теперь в ax — предыдущее число, в cx — следующая
Как вывести строку на экран в Ассемблере
Главная / Ассемблер / Примеры (исходники) /
|
Как выучить английский
В наше время любой человек должен знать английский язык. |
Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.
Правда, в современных воплощениях языка Ассемблера могут быть стандартные макросы или подпрограммы для вывода строк на экран. Однако начинающим будет полезно разобраться с тем, как это можно сделать только с помощью инструкций, без применения каких-либо облегчающих жизнь библиотек.
Итак, выводить строки на экран можно двумя путями:
- Посимвольно (то есть в цикле выводить каждый символ строки).
- Строку целиком.
Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:
- С помощью функций DOS.
- С помощью функций BIOS.
- Путём прямой записи в видеопамять.
Третий способ хорош тем, что он сразу записывает данные в видеопамять, что позволяет выполнять вывод более быстро. Однако в наше время он применим, разве что, в учебных целях. Потому что современные операционные системы не позволяют напрямую обращаться к “железу”.
Поэтому, если ваша программа является чуть более, чем учебной, то придётся
применять один из первых двух способов. Ну а если вам интересно, как выполнить
вывод непосредственно в видеопамять, то посмотреть примеры вы можете
здесь или
здесь.
Функции вывода DOS
Итак, начнём с функций вывода DOS. Эти функции являются функциями операционной системы DOS, но поддерживаются и операционными системами Windows.
Как я уже говорил, можно напечатать на экране строку в цикле, отдельно выводя каждый символ. Для этих целей можно использовать функции 02h, 06h или недокуметированное прерывание 29h.
Если требуется вывести на экран строку целиком, то можно применить функции 09h и 40h.
Для использования функций DOS надо сначала подготовить необходимые данные, записать номер функции в регистр AH, а затем вызвать прерывание 21h.
Все функции рассматривать здесь не будем. Для примера используем одну из самых простых функций DOS — функцию 09h, которая выводит в стандартное устройство вывода (в нашем случае — на экран) строку, адрес которой находится в DS:DX. Строка должна заканчиваться символом $, иначе функция не поймёт, где конец строки, и программа будет выводить много-много символов из памяти, начиная с указанного адреса.
Примерно так, как показано на рисунке:
Пример:
;========================================================== ; Эта программа выводит на экран строку "Hello, World!!!" ; с помощью функции DOS 09h ;---------------------------------------------------------- .model tiny .code ORG 100h ;начало СОМ-файла start: MOV AH, 09h ;Номер функции 09h MOV DX, offset stroka ;Адрес строки записываем в DX INT 21h RET ;завершение СОМ-файла stroka DB 'Hello, World!!!$' ;Строка для вывода END start ;==========================================================
При правильном использовании на экран будет выведено примерно следующее:
Функции вывода BIOS
Функции BIOS также могут выводить как отдельные символы (функции 09h, 0Ah, 0Eh), так и строки целиком (функция 13h).
Кроме того с помощью функций BIOS можно установить видеорежим, установить или считать положение курсора, а также считать символ и его атрибуты.
Хотя функции DOS тоже могут считывать символы, но всё-таки возможности BIOS более широки.
Для работы с функциями BIOS также сначала надо подготовить данные, записать номер функции в регистр AH, а затем вызвать прерывание 10h.Для примера рассмотрим функцию 13h. Перед вызовом функции надо:
- Записать номер функции в регистр АН.
- Записать режим вывода в регистр AL:
- бит 0: переместить курсор в конец строки после вывода.
- бит 1: строка содержит не только символы, но и атрибуты. Так что каждый символ описывается двумя байтами — ASCII-код и атрибут. Это можно использовать, если в строке символы должны иметь, например, разный цвет. Если атрибуты одинаковы для всей строки, то этот бит лучше сбросить (обнулить).
- биты 2-7: не используются.

- Записать длину строки в регистр СХ (только число символов, байты атрибутов не учитываются).
- Если строка содержит только символы, то записать в регистр BL атрибут. Этот атрибут будет применяться для всей строки.
- Записать координаты экрана, начиная с которых будет выводиться строка, в регистры DL (столбец — координата Х) и DH (строка — координата Y).
- Записать адрес начала строки в ES:BP.
Пример:
;========================================================== ; Эта программа выводит на экран строку "Hello, World!!!" ; с помощью функции BIOS 13h ;---------------------------------------------------------- .model tiny .code ORG 100h ;начало СОМ-файла start: MOV AH, 13h ;Номер функции 13h MOV AL, 1 ;Перевести курсор в конец строки MOV CX, 15 ;Длина строки MOV BL, 00011110b ;Жёлтый текст на синем фоне MOV DL, 5 ;Координата Х MOV DH, 2 ;Координата Y MOV BP, offset stroka ;Адрес строки записываем в DX INT 10h RET ;завершение СОМ-файла stroka DB 'Hello, World!!!' ;Строка для вывода END start ;==========================================================
Рассказывать об остальных функциях, а также об установке атрибутов и прочих вещах сегодня не буду.
Если кому интересно, то всё это можно найти в справочных материалах.
|
Подписаться на канал в РУТуб
Вступить в группу «Основы программирования» Подписаться на рассылки по программированию |
Учебный курс. Часть 22. Вывод чисел на консоль
В качестве примера программирования процедур займёмся такой важной проблемой, как вывод на консоль чисел в различных системах счисления. Проблема эта возникает потому, что в ассемблере нет никаких специальных средств для вывода чисел, а с помощью стандартных функций можно выводить только строки.
Следовательно, задача сводится к тому, чтобы преобразовать двоичное число в строку символов, а затем вывести эту строку на экран. Все процедуры в этой части являются лишь примерами, вы можете использовать их или написать свои собственные процедуры, более удобные для вас.
Для начала рассмотрим две полезные процедуры, которые будут использоваться в дальнейшем. Чтобы постоянно не обращаться в коде в функции 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
. |
Вывод чисел в двоичном виде
Алгоритм вывода в двоичном виде очень прост. Нужно проанализировать все биты числа и поместить в строку символы ‘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 символов). |
И наконец вот две процедуры, которые делают то, что нужно 🙂 Буфер имеет размер 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
. |
Полный исходный код примера вы можете скачать отсюда: 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 символа). |
Полный исходный код примера: 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. |
Для вывода байта можно преобразовать его в слово и воспользоваться той же процедурой:
;Процедура преобразования байта в строку в десятичном виде (без знака)
; 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.
Упражнение
Напишите программу для вывода на консоль массива слов со знаком в десятичном виде (например, через запятую). Для вывода чисел можете воспользоваться моим примером или написать свою собственную процедуру.
Результаты можете писать в комментариях.
Ещё раз ссылки на все примеры
- printbin.asm — вывод чисел на консоль в двоичном виде
- printoct.asm — вывод чисел на консоль в восьмеричном виде
- printdec.asm — вывод чисел на консоль в десятичном виде (со знаком и без знака)
- printhex.asm — вывод чисел на консоль в шестнадцатеричном виде
Следующая часть »
Сборка— Отображение чисел в DOS
Это правда, что DOS не предлагает нам функции для прямого вывода числа.
Вам придется сначала преобразовать число самостоятельно, а затем DOS отобразит его.
используя одну из функций вывода текста.
При решении задачи преобразования числа полезно увидеть, как
цифры, из которых состоит число, связаны друг с другом.
Рассмотрим число 65535 и его разложение:
(6*10000) + (5*1000) + (5*100) + (3*10) + (5*1)
Способ 1: деление по убывающей степени 10
Обработка числа слева направо удобна, т.
к.
позволяет нам отображать отдельную цифру, как только мы ее извлекли.
Разделив число (65535) на 10000 , мы получим однозначное частное (6) который мы можем сразу вывести как символ. Мы также получаем остаток (5535), которые станут дивидендом на следующем шаге.
Разделив остаток от предыдущего шага (5535) на 1000 , получаем однозначное частное (5), которое мы можем сразу вывести как символ. Мы также получаем остаток (535), который станет делимым на следующем шаге.
Разделив остаток от предыдущего шага (535) на 100 , получим однозначное частное (5), которое мы можем сразу вывести как символ. Мы также получаем остаток (35), который станет делимым на следующем шаге.
Разделив остаток от предыдущего шага (35) на 10 , получаем однозначное частное (3), которое мы можем сразу вывести как символ. Мы также получаем остаток (5), который станет делимым на следующем шаге.

Разделив остаток от предыдущего шага (5) на 1 , получим однозначное частное (5), которое мы можем сразу вывести как символ. Здесь остаток всегда будет 0. (Избегая этого глупого деления на 1 требуется дополнительный код)
mov bx,.Список
.a: xor dx, dx
раздел слова ptr [bx] ; -> AX=[0,9] — частное, остаток DX
хчг топор, дх
добавить dl,"0" ;превратить в символ [0,9] -> ["0","9"]
толкать топор ;(1)
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
pop ax ;(1) AX — следующее делимое
добавить бх,2
cmp bx,.List+10
джб .а
...
.Список:
дв 10000,1000,100,10,1
Хотя этот метод, конечно, даст правильный результат, он имеет некоторые недостатки. недостатки:
Рассмотрим меньшее число 255 и его разложение:
(0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)
Если бы мы использовали тот же 5-шаговый процесс, мы бы получили «00255».
Те 2 ведущих
нули нежелательны, и нам пришлось бы включать дополнительные инструкции, чтобы получить
избавиться от них.Делитель меняется с каждым шагом. Нам нужно было хранить список разделителей в Память. Динамическое вычисление этих делителей возможно, но вводит много дополнительных разделов.
Если бы мы хотели применить этот метод для отображения еще больших чисел, скажем 32-бит, и мы хотим, чтобы в конечном итоге задействованные подразделения получили действительно проблематично.
Таким образом, метод 1 непрактичен и поэтому используется редко.
Метод 2: деление на const 10
Обработка числа, идущего справа налево, кажется нелогичной так как наша цель — сначала отобразить крайнюю левую цифру. Но так как вы собираетесь узнайте, это прекрасно работает.
Разделив число (65535) на 10 , мы получим частное (6553), которое будет стать дивидендом на следующем шаге. Мы также получаем остаток (5), который мы пока не может выводить, поэтому нам придется где-то сохранять.
Стек представляет собой
удобное место для этого.Разделив частное из предыдущего шага (6553) на 10 , получим частное (655), которое станет делимым на следующем шаге. Мы также получаем остаток (3), который мы пока не можем вывести, поэтому нам придется его сохранить где-то. Стек — удобное место для этого.
Разделив частное из предыдущего шага (655) на 10 , получим частное (65), которое станет делимым на следующем шаге. Мы также получаем остаток (5), который мы еще не можем вывести, поэтому нам придется его сохранить где-то. Стек — удобное место для этого.
Разделив частное из предыдущего шага (65) на 10 , получим частное (6), которое станет делимым на следующем шаге. Мы также получаем остаток (5), который мы еще не можем вывести, поэтому нам придется его сохранить где-то. Стек — удобное место для этого.
Разделив частное из предыдущего шага (6) на 10 , получим частное (0), которое сигнализирует о том, что это было последнее деление.
Мы также получаем
остаток (6), который мы могли бы вывести как символ сразу, но воздержание от этого оказывается наиболее эффективным, и поэтому, как и прежде, мы
сохранить его в стеке.
На данный момент стек содержит наши 5 остатков, каждый из которых представляет собой одну цифру
число в диапазоне [0,9]. Поскольку стек работает по принципу LIFO (Last In First Out),
значение, которое мы будем POP first, является первой цифрой, которую мы хотим отобразить. Мы используем
отдельный цикл с 5 POP для отображения полного номера. Но на практике,
так как мы хотим, чтобы эта подпрограмма могла также работать с числами, которые
менее 5 цифр, мы будем считать цифры по мере их поступления, а затем сделаем это
много POP .
мов бх,10 ;КОНСТ
xor cx,cx ;Сбросить счетчик
.a: xor dx,dx ;Настройка деления DX:AX/BX
делитель бх; -> AX является частным, остаток DX=[0,9]
push dx ;(1) Сохраните остаток на данный момент
inc cx ;еще одна цифра
test ax,ax ;Частное равно нулю?
jnz .
a ;Нет, использовать в качестве следующего дивиденда
.b: поп дх ;(1)
добавить dl,"0" ;превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
петля .б
Этот второй метод не имеет недостатков первого метода:
- Поскольку мы останавливаемся, когда частное становится равным нулю, проблем никогда не возникает. с уродливыми ведущими нулями.
- Делитель зафиксирован. Это достаточно легко.
- Очень просто применить этот метод для отображения больших чисел и это именно то, что будет дальше.
На 8086 каскад из 2 делений необходим для деления 32-битного значения на DX:AX на 10.
1-е деление делит большее делимое (расширенное на 0), что дает
частное. 2-й отдел делит низкий дивиденд (расширенный с
остаток от 1-го деления), что дает низкий коэффициент. это остаток
из 2-го деления, которое мы сохраняем в стеке.
Чтобы проверить, является ли двойное слово в DX:AX нулем, я ИЛИ -ed обе половины в царапине
регистр.
Вместо подсчета цифр, требующего регистра, я решил поставить часовой в стеке. Потому что этот часовой получает значение (10), которое никакая цифра никогда не сможет have ([0,9]), это прекрасно позволяет определить, когда цикл отображения должен остановиться.
В остальном этот фрагмент аналогичен методу 2 выше.
мов бх,10 ;КОНСТ
нажать bx ;Страж
.a: mov cx,ax ;Временно сохранить LowDividend в CX
mov ax,dx ;Сначала разделить HighDividend
xor dx,dx ;Настройка для разделения DX:AX / BX
делитель бх; -> AX является HighQuotient, остаток используется повторно
xchg ax,cx ;Временно переместить в CX, восстановив LowDividend
делитель бх; -> AX имеет низкий коэффициент, остаток DX=[0,9]
push dx ;(1) Сохраните остаток на данный момент
mov dx,cx ;Построить истинное 32-битное частное в DX:AX
или cx,ax ;Истинное 32-битное частное равно нулю?
jnz .a ;Нет, использовать в качестве следующего дивиденда
pop dx ;(1a) Первый поп (точно цифра)
.
b: добавить dl,"0" ;превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
pop dx ;(1b) Все остальные попсы
cmp dx,bx ;Это был часовой?
jb .b ;Ещё нет
Процедура следующая:
Сначала выясните, является ли число со знаком отрицательным, проверив бит знака.
Если это так, то инвертируйте число и выведите символ «-», но будьте осторожны, чтобы не
уничтожьте номер DX:AX в процессе.
Остальная часть фрагмента такая же, как и для беззнакового номера.
test dx,dx ;Бит знака равен 15-му биту старшего слова
jns .a ;Это положительное число
отрицательный дх ;\
отрицательный топор; | Отменить DX:AX
сбб дх,0 ;/
толкать топор дх ;(1)
мов дл,"-"
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
поп дх топор ;(1)
.a: мов bx,10 ;CONST
нажать bx ;Страж
.b: mov cx,ax ;Временно сохранить LowDividend в CX
mov ax,dx ;Сначала разделить HighDividend
xor dx,dx ;Настройка для разделения DX:AX / BX
делитель бх; -> AX является HighQuotient, остаток используется повторно
xchg ax,cx ;Временно переместить в CX, восстановив LowDividend
делитель бх; -> AX имеет низкий коэффициент, остаток DX=[0,9]
push dx ;(2) Сохраните остаток на данный момент
mov dx,cx ;Построить истинное 32-битное частное в DX:AX
или cx,ax ;Истинное 32-битное частное равно нулю?
jnz .
b ;Нет, использовать в качестве следующего дивиденда
pop dx ;(2a) Первый поп (точно цифра)
.c: добавить dl,"0" ; Превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
pop dx ;(2b) Все остальные попсы
cmp dx,bx ;Это был часовой?
jb .c ;Ещё нет
В программе, где вам нужно время от времени отображать AL , AX или DX:AX , вы можете
просто включите 32-битную версию и используйте следующие маленькие обертки для меньших
размеры:
; ВХОД (аль) ВЫХОД ()
DisplaySignedNumber8:
толкать топор
cbw ;Повысить уровень AL до AX
вызов DisplaySignedNumber16
поп-топор
рет
; -------------------------
; ВХОД (ось) ВЫХОД ()
DisplaySignedNumber16:
толкать дх
cwd ;Повысить уровень AX до DX:AX
вызов DisplaySignedNumber32
поп дх
рет
; -------------------------
; ВХОД (dx:ax) ВЫХОД ()
DisplaySignedNumber32:
толкать топор bx cx dx
...
В качестве альтернативы, если вы не возражаете против затирания регистров AX и DX , используйте
это сквозное решение:
; IN (al) OUT () MOD (ax, dx)
DisplaySignedNumber8:
cbw
; --- --- --- --- -
; IN (ax) OUT () MOD (ax, dx)
DisplaySignedNumber16:
cwd
; --- --- --- --- -
; IN (dx:ax) OUT () MOD (ax,dx)
DisplaySignedNumber32:
нажать bx cx
.
..
integer — Сборка x8086 (emu8086) — Отображать 32-битное число на экране
это мой код (сборка x8086, а не MIPS, и я использую emu8086) для отображения 32-битного числа на экране. Конечно, основной алгоритм выглядит следующим образом:
Ввод: число Установить r=0,q=число,счетчик=0; в то время как q > 0 делать разделить q на 10 q <- Частное, r <- Остаток; нажать р; счетчик = счетчик + 1; конец, пока в то время как счетчик > 0 сделать поп р; счетчик = счетчик - 1; отображать конец, пока
Однако проблема в том, что на процессорах x8086 все регистры 16-битные. Таким образом, нет простого способа использовать 916 + q_low + q_high_redundant)*10 + r_low + r_high_redundant Теперь вам просто нужно разделить r_low + r_high_redundant и прибавить к частному, тогда вы получите результаты.
Вот мой код, пожалуйста, дайте мне несколько отзывов об эстетике, оптимизации кода,… большое спасибо:
; Автор Данг Ман Чыонг .стек 100ч .данные base_10 дв 10 var_32bits_high dw 0 var_32bits_low dw 0 quotidient_32bits_high dw 0 quotidient_32bits_low dw 0 negate_mask equ 0FFFFh low_signed_32bits_high dw 8000h low_signed_32bits_low dw 0000h low_signed_32bits_string dw "-2147483648$" qhigh дв 0 правый дв 0 qlow дв 0 рлоу дв 0 qhigh_redundant дв 0 rhigh_redundant дв 0 q_0 дв 0 qhigh0 экв 0h rhigh0 экв.0h qhigh2 экв 1999ч rhigh2 экв 6ч qhigh3 equ 3333h rhigh3 экв 2ч qhigh4 equ 4CCCh rhigh4 экв 8ч qhigh5 экв 6666h rhigh5 экв 4ч qhigh5 экв 8000ч rhigh5 экв. 0h qhigh6 экв 9999h rhigh6 экв 6ч qhigh7 equ 0B333h rhigh7 экв 2ч qhigh8 equ 0CCCCh rhigh8 экв 8ч qhigh9 экв 0E666h rhigh9 экв 4ч .код основной процесс ;Инициализация мов топор,@данные мов дс, топор ;пример: 7654321 = 0074CBB1h ; мов топор, 74 часа ; мов var_32bits_high, топор ; топор движения, 0CBB1h ; мов var_32bits_low, топор ;пример: 10223803 = 00931)= 80000000ч ; мов топор, 8000ч ; мов var_32bits_high, топор ; мов топор, 0000 ч ; мов var_32bits_low, топор ;пример: -1 = FFFF FFFFh мов топор, 0FFFFh мов var_32bits_high, топор мов топор, 0FFFFh мов var_32bits_low, топор мов топор, 0 mov bx,0 ;bx: quotidient_32bits_high mov dx,0 ;dx: quotidient_32bits_low мов сх,0 ;счетчик = 0 ;16бит или 32бит ? мов топор, var_32bits_high cmp топор, 0 jne _32bits_routine jmp _16bits_routine ;;; _32bits_routine: мов сх,0 ;если == -2147483648 (-2^31) мов топор, var_32bits_high cmp топор, lowest_signed_32bits_high jne check_if_neg мов топор, var_32bits_low cmp топор, lowest_signed_32bits_low jne check_if_neg ;тогда lea dx, lowest_signed_32bits_string мов ах, 9через 21 час jmp return_to_dos ;если < 0 check_if_neg: мов топор, var_32bits_high cmp топор, 0 JNL препараты ; затем выведите "-" .
.. мов ах, 2 мов дл,'-' через 21 час ;... и отрицание числа мов топор, var_32bits_high xor топор,negate_mask мов var_32bits_high, топор мов топор, var_32bits_low xor топор,negate_mask топор мов var_32bits_low, топор ИНЦ препараты мов топор, var_32bits_high топор мов var_32bits_high, топор препараты: мов топор, var_32bits_high мов quotidient_32bits_high, топор мов топор, var_32bits_low мов quotidient_32bits_low, топор в то время как_32bits: ; в то время как >0 делать mov топор,quotidient_32bits_high cmp топор, 0 jne div_high_part mov топор,quotidient_32bits_low cmp топор, 0 jne div_high_part jmp print_char div_high_part: ; разделить старшую часть мов дх,0 mov топор,quotidient_32bits_high div base_10 мов qhigh, топор мов правый, дх ;правый корпус мов топор, правый cmp топор, 0 je _rhigh0 см ось, 1 je _rhigh2 cmp топор, 2 je _rhigh3 cmp топор, 3 je _rhigh4 смп топор, 4 je _rhigh5 см ось, 5 je _rhigh5 смп топор, 6 je _rhigh6 смп топор, 7 je _rhigh7 смп топор, 8 je _rhigh8 смп топор,9je _rhigh9 _rhigh0: мов топор, qhigh0 мов qhigh_redundant, топор мов топор, rhigh0 мов rhigh_redundant, топор jmp _aftercase _rhigh2: мов топор, qhigh2 мов qhigh_redundant, топор мов топор, rhigh2 мов rhigh_redundant, топор jmp _aftercase _rhigh3: мов топор, qhigh3 мов qhigh_redundant, топор мов топор, rhigh3 мов rhigh_redundant, топор jmp _aftercase _rhigh4: топор, qhigh4 мов qhigh_redundant, топор мов топор, rhigh4 мов rhigh_redundant, топор jmp _aftercase _rhigh5: топор, qhigh5 мов qhigh_redundant, топор мов топор, rhigh5 мов rhigh_redundant, топор jmp _aftercase _rhigh5: топор, qhigh5 мов qhigh_redundant, топор мов топор, rhigh5 мов rhigh_redundant, топор jmp _aftercase _rhigh6: топор, qhigh6 мов qhigh_redundant, топор мов топор, rhigh6 мов rhigh_redundant, топор jmp _aftercase _rhigh7: топор, qhigh7 мов qhigh_redundant, топор мов топор, rhigh7 мов rhigh_redundant, топор jmp _aftercase _rhigh8: топор, qhigh8 мов qhigh_redundant, топор мов топор, rhigh8 мов rhigh_redundant, топор jmp _aftercase _rhigh9: мов топор, qhigh9 мов qhigh_redundant, топор мов топор, rhigh9 мов rhigh_redundant, топор jmp _aftercase _афтеркейс: ;разделить нижнюю часть мов топор, 0 мов q_0, топор мов дх,0 mov топор,quotidient_32bits_low div base_10 мов qlow, топор мов рлоу, дх мов топор, рлоу добавить топор, rhigh_redundant ;если остаток >= 10 cmp топор, base_10 jl after_if вспомогательная ось, base_10 мов дх, 1 мов q_0, дх после_если: мов рлоу, топор мов топор, q_0 добавить топор, qlow мов qlow, топор jnc лейбл1 мов топор, qhigh топор мов qhigh, топор метка1: мов топор, qlow добавить топор, qhigh_redundant мов qlow, топор JNC лейбл2 мов топор, qhigh топор мов qhigh, топор метка2: ;поместить остаток в стек мов топор, рлоу толкать топор вкл сх мов топор, qhigh мов quotidient_32bits_high, топор мов топор, qlow мов quotidient_32bits_low, топор jmp в то время как_32bits ;;; _16bits_routine: мов топор, var_32bits_low mov bx,0 ;bx: частное мов сх,0 пока_цикл: cmp топор, 0 je print_char мов дх,0 div base_10 mov bx,ax ;ax сохраняет частное mov ax,dx ;dx сохраняет остаток ;протолкнуть остаток толкать топор ;счетчик = счетчик + 1 вкл сх ;числитель = частное мов топор, бх jmp while_loop print_char: cmp сх,0 je return_to_dos поп-топор ;потому что в этот момент 0 <= ax <= 9, установка ah = 2 не меняет результаты мов ах, 2 мов дл, аль добавить dl,30h ;0-> '0',1->'1',.

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

..
endline db 13,10,'$'
Значение регистра не сохраняется.
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
..
buffer rb 17
Значение регистра не сохраняется.
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
wtuds_lp2: ;Цикл извлечения символов из стека
pop dx ;Восстановление символа из стека
mov [di],dl ;Сохранение символа в буфере
inc di ;Инкремент адреса буфера
loop wtuds_lp2 ;Команда цикла
pop bx
pop dx
pop cx
pop ax
ret
Те 2 ведущих
нули нежелательны, и нам пришлось бы включать дополнительные инструкции, чтобы получить
избавиться от них.
Стек представляет собой
удобное место для этого.
Мы также получаем
остаток (6), который мы могли бы вывести как символ сразу, но воздержание от этого оказывается наиболее эффективным, и поэтому, как и прежде, мы
сохранить его в стеке.
a ;Нет, использовать в качестве следующего дивиденда
.b: поп дх ;(1)
добавить dl,"0" ;превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
петля .б
b: добавить dl,"0" ;превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
pop dx ;(1b) Все остальные попсы
cmp dx,bx ;Это был часовой?
jb .b ;Ещё нет
b ;Нет, использовать в качестве следующего дивиденда
pop dx ;(2a) Первый поп (точно цифра)
.c: добавить dl,"0" ; Превратить в символ [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
интервал 21ч ; -> АЛ
pop dx ;(2b) Все остальные попсы
cmp dx,bx ;Это был часовой?
jb .c ;Ещё нет
..
0h
qhigh2 экв 1999ч
rhigh2 экв 6ч
qhigh3 equ 3333h
rhigh3 экв 2ч
qhigh4 equ 4CCCh
rhigh4 экв 8ч
qhigh5 экв 6666h
rhigh5 экв 4ч
qhigh5 экв 8000ч
rhigh5 экв. 0h
qhigh6 экв 9999h
rhigh6 экв 6ч
qhigh7 equ 0B333h
rhigh7 экв 2ч
qhigh8 equ 0CCCCh
rhigh8 экв 8ч
qhigh9 экв 0E666h
rhigh9 экв 4ч
.код
основной процесс
;Инициализация
мов топор,@данные
мов дс, топор
;пример: 7654321 = 0074CBB1h
; мов топор, 74 часа
; мов var_32bits_high, топор
; топор движения, 0CBB1h
; мов var_32bits_low, топор
;пример: 10223803 = 00931)= 80000000ч
; мов топор, 8000ч
; мов var_32bits_high, топор
; мов топор, 0000 ч
; мов var_32bits_low, топор
;пример: -1 = FFFF FFFFh
мов топор, 0FFFFh
мов var_32bits_high, топор
мов топор, 0FFFFh
мов var_32bits_low, топор
мов топор, 0
mov bx,0 ;bx: quotidient_32bits_high
mov dx,0 ;dx: quotidient_32bits_low
мов сх,0 ;счетчик = 0
;16бит или 32бит ?
мов топор, var_32bits_high
cmp топор, 0
jne _32bits_routine
jmp _16bits_routine
;;;
_32bits_routine:
мов сх,0
;если == -2147483648 (-2^31)
мов топор, var_32bits_high
cmp топор, lowest_signed_32bits_high
jne check_if_neg
мов топор, var_32bits_low
cmp топор, lowest_signed_32bits_low
jne check_if_neg
;тогда
lea dx, lowest_signed_32bits_string
мов ах, 9через 21 час
jmp return_to_dos
;если < 0
check_if_neg:
мов топор, var_32bits_high
cmp топор, 0
JNL препараты
; затем выведите "-" .
..
мов ах, 2
мов дл,'-'
через 21 час
;... и отрицание числа
мов топор, var_32bits_high
xor топор,negate_mask
мов var_32bits_high, топор
мов топор, var_32bits_low
xor топор,negate_mask
топор
мов var_32bits_low, топор
ИНЦ препараты
мов топор, var_32bits_high
топор
мов var_32bits_high, топор
препараты:
мов топор, var_32bits_high
мов quotidient_32bits_high, топор
мов топор, var_32bits_low
мов quotidient_32bits_low, топор
в то время как_32bits:
; в то время как >0 делать
mov топор,quotidient_32bits_high
cmp топор, 0
jne div_high_part
mov топор,quotidient_32bits_low
cmp топор, 0
jne div_high_part
jmp print_char
div_high_part:
; разделить старшую часть
мов дх,0
mov топор,quotidient_32bits_high
div base_10
мов qhigh, топор
мов правый, дх
;правый корпус
мов топор, правый
cmp топор, 0
je _rhigh0
см ось, 1
je _rhigh2
cmp топор, 2
je _rhigh3
cmp топор, 3
je _rhigh4
смп топор, 4
je _rhigh5
см ось, 5
je _rhigh5
смп топор, 6
je _rhigh6
смп топор, 7
je _rhigh7
смп топор, 8
je _rhigh8
смп топор,9je _rhigh9
_rhigh0:
мов топор, qhigh0
мов qhigh_redundant, топор
мов топор, rhigh0
мов rhigh_redundant, топор
jmp _aftercase
_rhigh2:
мов топор, qhigh2
мов qhigh_redundant, топор
мов топор, rhigh2
мов rhigh_redundant, топор
jmp _aftercase
_rhigh3:
мов топор, qhigh3
мов qhigh_redundant, топор
мов топор, rhigh3
мов rhigh_redundant, топор
jmp _aftercase
_rhigh4:
топор, qhigh4
мов qhigh_redundant, топор
мов топор, rhigh4
мов rhigh_redundant, топор
jmp _aftercase
_rhigh5:
топор, qhigh5
мов qhigh_redundant, топор
мов топор, rhigh5
мов rhigh_redundant, топор
jmp _aftercase
_rhigh5:
топор, qhigh5
мов qhigh_redundant, топор
мов топор, rhigh5
мов rhigh_redundant, топор
jmp _aftercase
_rhigh6:
топор, qhigh6
мов qhigh_redundant, топор
мов топор, rhigh6
мов rhigh_redundant, топор
jmp _aftercase
_rhigh7:
топор, qhigh7
мов qhigh_redundant, топор
мов топор, rhigh7
мов rhigh_redundant, топор
jmp _aftercase
_rhigh8:
топор, qhigh8
мов qhigh_redundant, топор
мов топор, rhigh8
мов rhigh_redundant, топор
jmp _aftercase
_rhigh9:
мов топор, qhigh9
мов qhigh_redundant, топор
мов топор, rhigh9
мов rhigh_redundant, топор
jmp _aftercase
_афтеркейс:
;разделить нижнюю часть
мов топор, 0
мов q_0, топор
мов дх,0
mov топор,quotidient_32bits_low
div base_10
мов qlow, топор
мов рлоу, дх
мов топор, рлоу
добавить топор, rhigh_redundant
;если остаток >= 10
cmp топор, base_10
jl after_if
вспомогательная ось, base_10
мов дх, 1
мов q_0, дх
после_если:
мов рлоу, топор
мов топор, q_0
добавить топор, qlow
мов qlow, топор
jnc лейбл1
мов топор, qhigh
топор
мов qhigh, топор
метка1:
мов топор, qlow
добавить топор, qhigh_redundant
мов qlow, топор
JNC лейбл2
мов топор, qhigh
топор
мов qhigh, топор
метка2:
;поместить остаток в стек
мов топор, рлоу
толкать топор
вкл сх
мов топор, qhigh
мов quotidient_32bits_high, топор
мов топор, qlow
мов quotidient_32bits_low, топор
jmp в то время как_32bits
;;;
_16bits_routine:
мов топор, var_32bits_low
mov bx,0 ;bx: частное
мов сх,0
пока_цикл:
cmp топор, 0
je print_char
мов дх,0
div base_10
mov bx,ax ;ax сохраняет частное
mov ax,dx ;dx сохраняет остаток
;протолкнуть остаток
толкать топор
;счетчик = счетчик + 1
вкл сх
;числитель = частное
мов топор, бх
jmp while_loop
print_char:
cmp сх,0
je return_to_dos
поп-топор
;потому что в этот момент 0 <= ax <= 9, установка ah = 2 не меняет результаты
мов ах, 2
мов дл, аль
добавить dl,30h ;0-> '0',1->'1',.