Разное

Ассемблер вывод числа на экран: Решение: Вывод числа на экран

Assembler вывод числа на экран — ПК портал

Содержание

  1. Функции вывода DOS
  2. Функции вывода BIOS
  3. Вспомним о процедурах
  4. Вывод на экран
  5. Запуск программы
  6. ЗАДАЧА 1
  7. РЕШЕНИЕ
  8. ЗАДАЧА 2
  9. РЕШЕНИЕ
Как выучить английский

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

Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.

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

Итак, выводить строки на экран можно двумя путями:

  1. Посимвольно (то есть в цикле выводить каждый символ строки).
  2. Строку целиком.

Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:

  1. С помощью функций DOS.
  2. С помощью функций BIOS.
  3. Путём прямой записи в видеопамять.

Третий способ хорош тем, что он сразу записывает данные в видеопамять, что позволяет выполнять вывод более быстро. Однако в наше время он применим, разве что, в учебных целях. Потому что современные операционные системы не позволяют напрямую обращаться к “железу”.

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

Функции вывода 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: не используются.
  • Записать длину строки в регистр СХ (только число символов, байты атрибутов не учитываются).
  • Если строка содержит только символы, то записать в регистр BL атрибут. Этот атрибут будет применяться для всей строки.
  • Записать координаты экрана, начиная с которых будет выводиться строка, в регистры DL (столбец — координата Х) и DH (строка — координата Y).
  • Записать адрес начала строки в ES:BP.
  • Рассказывать об остальных функциях, а также об установке атрибутов и прочих вещах сегодня не буду. Если кому интересно, то всё это можно найти в справочных материалах.

    Сегодня мы рассмотрим тему «Вывод на экран 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 — следующая

    Как вывести строку на экран в Ассемблере

    Главная / Ассемблер / Примеры (исходники) /


    Как выучить английский

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

    Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.

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

    Итак, выводить строки на экран можно двумя путями:

    1. Посимвольно (то есть в цикле выводить каждый символ строки).
    2. Строку целиком.

    Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:

    1. С помощью функций DOS.
    2. С помощью функций BIOS.
    3. Путём прямой записи в видеопамять.

    Третий способ хорош тем, что он сразу записывает данные в видеопамять, что позволяет выполнять вывод более быстро. Однако в наше время он применим, разве что, в учебных целях. Потому что современные операционные системы не позволяют напрямую обращаться к “железу”.

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

    Функции вывода 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
    . ..
    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.

    Упражнение

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

    Ещё раз ссылки на все примеры

    • 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',.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *