Учебный курс. Часть 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 — адрес строки 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,'$' |
;Процедура вывода конца строки (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 ;Возврат из процедуры |
;Процедура преобразования байта в строку в двоичном виде ; 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 |
;Процедура преобразования слова в строку в двоичном виде ; 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 |
;Процедура вывода байта на консоль в двоичном виде ; 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-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 |
;Процедура преобразования байта в строку в шестнадцатеричном виде ; 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 |
;Процедура преобразования слова в строку в шестнадцатеричном виде ; 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 |
;Процедура преобразования слова в строку в десятичном виде (без знака) ; 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 |
;Процедура преобразования байта в строку в десятичном виде (без знака) ; 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 |
;Процедура преобразования слова в строку в десятичном виде (со знаком) ; 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 |
;Процедура преобразования байта в строку в десятичном виде (со знаком) ; 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 — вывод чисел на консоль в шестнадцатеричном виде
Следующая часть »
asmworld.ru
Решение: Вывод числа на экран
Результат вывести на экран в шестнадцатеричном виде. Результат вывести на экран в двоичном виде.masm .model small .data db "Hello" a1 db 2 ;Переменная a c1 db 2 ;Переменная c d1 db 2 ;Переменная d x1 dw ? Denominator dw ? ;Знаменатель Numerator dw ? ;Числитель .code start: mov ax,@data mov ds,ax ;знаменатель - Denominator ------------------------------------------------ mov al,a1 Imul al ; <ax>=a*a dec ax ; <ax>=a*a-1 ; подготовка к расширению d (BYTE===>WORD) push ax ; Запоминание <ax>=a*a-1 в стеке mov al,d1 CBW ; al ===> AX mov SI,ax ; <SI>=<ax>=d - нужно для ЧИСЛИТЕЛЯ!!! pop bx ;Восстановление содержимого стека ===> <bx>=a*a-1 add bx,ax ; <bx>=a*a-1+d mov Denominator,bx ;числитель - Numerator ---------------------------------------------------- mov ax, 25 IDIV c1 ; <aL>=25/c CBW ; al ===> AX sub ax,SI ; <ax>=25/c-d add ax,2 ; <ax>=25/c-d+2 CWD ; aX ===> DX:AX mov WORD PTR Numerator, AX ; мл. часть числителя mov WORD PTR Numerator+2, DX ; ст. часть числителя ;РЕЗУЛЬТАТ IDIV bx ; <ax>=(25/c-d+2)/(a*a-1+d) mov x1,ax ;ВЫХОДИМ mov ah,4Ch mov al,00h int 21h end startПример вывод числа в 16-ричной системе счисления на экран. Размерность числа – 1 байт (8 бит).
masm .model small .stack 256 .data a db 2Ch ;Исходное число letters db '0123456789ABCDEF$' ;Числа в 16-ричной системе ;Сообщения mes db "Число в 16-ричной системе - $" mes2 db 0Dh,0Ah,'$' .code start: ;ИНИЦИАЛИЗАЦИЯ ПРОГРАММЫ mov ax,@data mov ds,ax ;ВЫВОД НАЧАЛЬНОГО СООБЩЕНИЯ mov ah,09h mov dx,offset mes int 21h ;ВЫВОД ЧИСЛА В 16-РИЧНОЙ СИСТЕМЕ xor ax,ax mov al,a push ax shr al,4 ; выделить старшие четыре бита xor bx,bx mov bl,al mov al,letters[bx] int 29h ; вывести на экран pop ax and al,0Fh ; выделить младшие четыре бита xor bx,bx mov bl,al mov al,letters[bx] int 29h ; вывести на экран ;ВЫВОД ЗНАКА "КОНЕЦ СТРОКИ" mov ah,09h mov dx,offset mes2 int 21h ;ВЫХОД mov ah,4Ch mov al,00h int 21h end startвообще не понимаю как это делать…помогите ребят
studassistent.ru
Как вывести строку на экран в Ассемблере
Главная / Ассемблер / Примеры (исходники) /
Как выучить английский
В наше время любой человек должен знать английский язык. А тем более программист. Потому что довольно большая часть документации существует только на английском. А профессионал не может ждать, пока кто-то переведёт ему документацию. Он должен уметь быстро получать нужную инфорамцию и использовать её, независимо от того, на каком она языке — на английском или на русском… Ссылки на курсы по английскому… |
Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.
Правда, в современных воплощениях языка Ассемблера могут быть стандартные макросы или подпрограммы для вывода строк на экран. Однако начинающим будет полезно разобраться с тем, как это можно сделать только с помощью инструкций, без применения каких-либо облегчающих жизнь библиотек.
Итак, выводить строки на экран можно двумя путями:
- Посимвольно (то есть в цикле выводить каждый символ строки).
- Строку целиком.
Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:
- С помощью функций 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 ;==========================================================
Рассказывать об остальных функциях, а также об установке атрибутов и прочих вещах сегодня не буду. Если кому интересно, то всё это можно найти в справочных материалах.
av-assembler.ru
Первый вывод на экран Assembler
Сегодня мы рассмотрим тему «Вывод на экран Assembler» и разберем программу, которая сможет выводить на экран нашей консоли числа. Для этого нам потребуется изучить несколько новых процедур, а также познакомится с подключаемыми библиотеками и системными файлами.
Вспомним о процедурах
Для начала, я напомню вам, что когда мы изучали с вами процедуры Assembler, то я, подводя итоги, написал, что для процедур с параметрами необходим прототип. Да, это так, но есть одно но: если эта процедура не является стандартной, и не описана где нибудь в подключаемой библиотеки.
Так вот, ранее изученная процедура ExitProcess, а также те процедуры, которые мы сегодня с вами изучим(WriteConsoleA, wsprintf) являются стандартными(на самом деле такого термина нет, но думаю так понятнее), и для них не нужно писать прототип.
Вывод на экран
А теперь перейдем к коду:
.386 .model flat,stdcall option casemap:none include ..\INCLUDE\kernel32.inc include ..\INCLUDE\user32.inc includelib ..\LIB\kernel32.lib includelib ..\LIB\user32.lib
Как вы заметили, у нас увеличилось число подключаемых файлов. И как я уже выше написал, все они понадобятся для того, чтобы работали процедуры, которые мы будем использовать далее в этой программе.
BSIZE equ 15 ; так задается константа BSIZE .data ifmt db "%d", 0 ;строка формата buf db BSIZE dup(?) ;буфер msg dd 123456 ;то что мы выводим stdout dd ? cWritten dd ?
Далее будет не очень понятно пока не увидим в действии, но все же попробую объяснить. Первое, что мы делаем, объявляем константу BSIZE со значением 15. Затем в разделе .data , там где объявляются переменные, задаем несколько переменных, которые потребуются как параметры для наших дальнейших процедур.
Стоит отметить переменную buf: запись dup(duplication) говорит о подряд идущих байт, которые выделяются под переменную buf размерностью BSIZE(15). А знак вопроса говорит, что значения байт заранее не определено.
Итак, пока не все понятно, поэтому идем дальше, тут у вас должен сложиться пазл:
.code start: invoke GetStdHandle, -11 ; дескриптор вывода mov stdout,eax ; по умолчанию помещается в eax invoke wsprintf, ADDR buf, ADDR ifmt, msg invoke WriteConsoleA, stdout, ADDR buf, BSIZE, ADDR cWritten, 0 invoke ExitProcess,0 end start
Для того, чтобы вывести что нибудь на экран нужно знать дескриптор вывода. Процедура GetStdHandle считывает адрес дескриптора экрана, и по умолчанию записывает его в регистр eax. После, мы записываем это значение из регистра в переменную stdout.
Дальше мы используем процедуру wsprintf. Она нужна для того, чтобы вывести наше сообщение(именно число) в понятном для нас формате, то есть эта процедура преобразования машинного языка в человеческий формат. Для этого используется переменная ifmt.
Ну и за сам вывод отвечает процедура WriteConsoleA. Она принимает 5 параметров, и последним всегда должен идти 0.
Мой совет: лучше запомнить эти процедуры в таком виде, каком они представлены здесь, в наших дальнейших статьях мы практически всегда будем их использовать.
Запуск программы
А сейчас я покажу вам как все это реализовать на практике, после написания кода.
Я создал файл fifth.asm и поместил его в папку BIN. Затем я открываю командную строку, и перехожу в папку BIN с помощью команды cd C:\Users\Никита\BIN (у вас будет другой путь, скорее всего)
Далее компилирую наш файл, прописывая команду amake.bat fifth, если код правильный, то будет как на фото:
Как мы видим, все скомпилировалось без ошибок, но ничего не вывелось на экран. Для того, чтобы наша программа выполнилась, нужно после компиляции запустить файл с расширением .exe(он создается автоматически после правильной компиляции). Прописываем в командной строке fifth.exe, у вас должно получится нечто подобное:
Все прошло успешно! На сегодня на этом закончим c выводом на экран, но в следующих примерах Assembler мы еще будем возвращаться к этой теме, если у вас остались вопросы, то пишите их в комментариях, не забывайте просматривать исходники и предыдущие статьи.
Скачать исходники
Поделиться ссылкой:
Похожее
codetown.ru
Решение: Вывод введенного числа в TASM
Здравстуйте, никак не могу разобраться с ассемблером на примере кода. Нужно вывести введенное число с текстом «Вы ввели» И нужно сделать проверку это то число или нет, если то — то пусть выводится «нажмите любую клавишу» если нет «то введите число еще раз» . Пожалуйста, очень срочноdata segment para public "data" ;начало сегмента данных message db 'Enter: $' ;наша строка (приглашение для ввода двух символов с клавы) data ends ;конец сегмента данных stk segment stack ;начало сегмента стека db 256 dup ("?") ;выделение памяти под стек stk ends ;конец сегмента стека code segment para public "code" ;начало сегмента кода main proc ;начало процедуры assume cs:code,ds:data,ss:stk ;сопоставление сегментных регистров и сегментов mov ax,data mov ds,ax ;инициализация сегмента данных через промежуточный регистр ax mov ah,9 ;функция вывода сообщения на экран mov dx,offset message ;адрес начала сообщения int 21h ;прерывание, чтобы функция выполнилась xor ax,ax ;обнуление регистра ax (здесь это не обязательно, можно удалить эту строку) mov ah,1h ;функция ввода с клавиатуры символа с эхом int 21h ;прерывание, чтобы функция выполнилась ;ASCII-код введенного символа помещается в регистр al mov dl,al ;помещаем ASCII-код введенного символа в регистр dl sub dl,30h ;предполагаем, что была нажата цифра. преобразовываем ASCII-код цифры в символ cmp dl,9h ;проверяем, действительно ли была нажата цифра jle M1 ;если была нажата цифра, то прыгаем на метку M1 sub dl,7h ;если была нажата буква, то дополнительно преобразовываем ASCII-код буквы в символ M1: ;метка M1 mov cl,4h ;один шестнадцатеричный символ занимает 4 бита (полубайт) shl dl,cl ;сдвигаем символ из младшего полубайта в старший int 21h ;вводим второй символ. прерывание, чтобы функция ввода выполнилась sub al,30h ;предполагаем, что была нажата цифра. преобразовываем ASCII-код цифры в символ cmp al, 9h ;проверяем, действительно ли была нажата цифра jle M2 ;если была нажата цифра, то прыгаем на метку M2 sub al,7h ;если была нажата буква, то дополнительно преобразовываем ASCII-код буквы в символ M2: ;метка M2 add dl,al ;помещаем символ в младший полубайт mov ax,4c00h;функция выхода из программы. код возврата - ноль int 21h ;прерывание, чтобы функция выполнилась main endp ;конец процедуры code ends ;конец сегмента кода end main ;указывается метка (процедура), с которой должна начаться выполняться программа
studassistent.ru
ASM вывод числа на экран : Программирование
.include «Tn13def.inc»
; функции ножек
.equ _PIN_PC_OUT = PB2 ; 7 — й PIN — вывод данных на комп
.equ _PIN_RELE_OUT1 = PB3 ; 2 — й PIN — выход на обмотку 1
.equ _PIN_RELE_OUT2 = PB1 ; 6 — й PIN — выход на обмотку 2
.equ _PIN_RELE_IN1 = PB4 ; 3 — й PIN — вход контакта 1
.equ _PIN_RELE_IN2 = PB0 ; 5 — й PIN — вход контакта 2
; регистры общего назначения
.def _r_mom = r16 ; служебный регистр для записи в порты
.def _r_WAIT = r17
.def _r_count_measure_sets = r18
.def _r_transmit_data = r19
.def _r_t1_lb = r20
.def _r_t1_hb = r21
.def _r_t1_last_lb = r22
.def _r_t1_last_hb = r23
.def _r_t2_lb = r24
.def _r_t2_hb = r25
.def _r_t2_last_lb = r26
.def _r_t2_last_hb = r27
.def _r_count_measure_time_lb = r28
.def _r_count_measure_time_hb = r29
; переменные
.equ _one_measure_time = 96 ; тактов = 10us — дискретность измерения
.equ _max_measure_time = 3000 ; циклов по 10us = 30ms — интервал измерения
.equ _sets_of_measures = 10 ; количество измерений (1 измерение = туда + обратно)
;.equ _RS232_time = 1000 ; тактов бита RS232 для скорости 9600 бод
.equ _RS232_time = 250 ; тактов бита RS232 для скорости 38400 бод
; для десятичной системы исчисления
.equ _SI_order = 10 ; для десятичной системы исчисления при выводе результатов
;.equ _max_length_SI = 5 ; макс. разрядность чисел в _SI_order
.equ _SI_order_0 = 1
.equ _SI_order_1 = _SI_order
.equ _SI_order_2 = _SI_order_1*_SI_order_1
.equ _SI_order_3 = _SI_order_2*_SI_order_1
.equ _SI_order_4 = _SI_order_3*_SI_order_1
; нужные коды KOI_8
.equ KOI_8_comma = $2C ; код «,»
.equ KOI_8_semicolon = $3B ; код «;»
.equ KOI_8_ps = $0A ; код «перевод строки»
.equ KOI_8_vk = $0D ; код «возврат каретки»
;———————————————————————
rjmp RESET ; Reset Handler
reti ;rjmp EXT_INT0 ; IRQ0 Handler
reti ;rjmp PIN_CHANGE ; PCINT0 Handler
reti ;rjmp TIM0_OVF ; Timer0 Overflow Handler
reti ;rjmp EE_RDY ; EEPROM Ready Handler
reti ;rjmp ANA_COMP ; Analog Comparator Handler
reti ;rjmp TIM0_COMPA ; Timer0 CompareA Handler
reti ;rjmp TIM0_COMPB ; Timer0 CompareB Handler
reti ;rjmp WATCHDOG ; Watchdog Interrupt Handler
reti ;rjmp ADCCONV ; ADC Conversion Handler
;———————————————————————
RESET:
ldi _r_mom, (1<<_PIN_RELE_OUT1) + (1<<_PIN_RELE_OUT2) + (1<<_PIN_PC_OUT)
out DDRB, _r_mom ; пины выходов
ldi _r_mom, (1<<_PIN_RELE_IN1) + (1<<_PIN_RELE_IN2)
out PORTB, _r_mom ; подтягиваюие резисторы на входы
; ldi _r_mom, (1<<_PIN_RELE_IN1) + (1<<_PIN_RELE_IN2) + (1<<_PIN_RELE_OUT1) + (1<<_PIN_RELE_OUT2)
; out PCMSK, _r_mom ; маска пинов для прерываний по pin change
rjmp MAIN
;———————————————————————
; прерывание по изменению состояния выводов
;PIN_CHANGE:
; clr _r_mom
; out GIMSK, _r_mom
; reti
;———————————————————————
WAIT_Idle:
; для относительно длинных пауз
; ждем в состоянии Idle ПРИМЕРНО _r_WAIT / 37.5 секунд
cli
; clr _r_mom
; out GIMSK, _r_mom
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0) + (1<<SM1)
sbr _r_mom, (1<<SE)
;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
out MCUCR, _r_mom ; задаем режим sleep-а Idle и устанавливаем флаг его разрешения
ldi _r_mom, 250
out OCR0A, _r_mom ; значение счетчика таймера для прерывания
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_mom, (1<<OCIE0A)
out TIMSK0, _r_mom ; разрешаем прерывание по счетчику таймера
ldi _r_mom, (1<<CS02) + (0<<CS01) + (1<<CS00)
out TCCR0B, _r_mom ; запускаем таймер с частотой СК/1024
sei
WAIT_Idle_sleep:
sleep ; Idle
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
dec _r_WAIT
brne WAIT_Idle_sleep
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; сбрасываем флаг разрешения sleep-а
clr _r_mom
out TIMSK0, _r_mom ; запрещаем прерывание по счетчику таймера
out TCCR0B, _r_mom ; останавливаем таймер
out OCR0A, _r_mom ; нулевое значение счетчика таймера для прерывания
out TCNT0, _r_mom ; сброс счетчика таймера
ret
;;———————————————————————
;
;EEPROM_write:
;
; sbic EECR, EEPE
; rjmp EEPROM_write
;
; ; Set Programming mode
; ldi _r_mom, (0<<EEPM1)|(0<<EEPM0)
; out EECR, _r_mom
;
; out EEARL, _r_EEPROM_address
; out EEDR, _r_EEPROM_data
; sbi EECR, EEMPE
; sbi EECR, EEPE
; ret
;;———————————————————————
;
;EEPROM_read:
;
; sbic EECR, EEPE
; rjmp EEPROM_read
; out EEARL, _r_EEPROM_address
; sbi EECR, EERE
; in _r_EEPROM_data, EEDR
; ret
;;———————————————————————
DECIMAL_value_KOI_8:
; _r_t1_hb:_r_t1_lb — остаток от числа, _SI_order-ичный разряд которого надо получить
; _r_count_measure_time_hb:_r_count_measure_time_lb — число _SI_order в нужной степени
; результат — в _r_transmit_data (и уже +$30 к цифре для получения её кода KOI-8)
clr _r_transmit_data
DECIMAL_value_KOI_8_1:
cp _r_t1_lb, _r_count_measure_time_lb
cpc _r_t1_hb, _r_count_measure_time_hb
brlo DECIMAL_value_KOI_8_END
sub _r_t1_lb, _r_count_measure_time_lb
sbc _r_t1_hb, _r_count_measure_time_hb
inc _r_transmit_data
rjmp DECIMAL_value_KOI_8_1
DECIMAL_value_KOI_8_END:
ldi _r_mom, $30 ; свиг для кода KOI-8 цифры
add _r_transmit_data, _r_mom
ret
;———————————————————————
PAUSE_RS232:
; _RS232_time — 13 (тактов на операции при передаче) — 5 (вызов rcall) — 2 (ret)
; итог / 5 (тактов каждого цикла) = в счетчик
ldi _r_mom, (_RS232_time — 13 — 5 — 2) / 5;
PAUSE_RS232_Count:
dec _r_mom
nop
nop
brne PAUSE_RS232_Count
ret
;———————————————————————
DATA_transmit_RS232:
; содержимое _r_transmit_data — на комп по RS232
cli
; отправляем посылку RS232
; 13 тактов операций после каждого бита + (_RS232_time — 9) тактов паузы
cbi PORTB, _PIN_PC_OUT ; старт-бит, до сего момента у нас была 1-ца и долго
rcall PAUSE_RS232
nop
nop
nop
nop
nop
nop
; 8 бит данных и 2 стоп бита = 10 бит
; для посылки их в одном цикле инвертируем байт даннных
; и будем посылать инвертированные биты — 2 стоп бита получатся
; как инвертированные нули при логическом сдвиге вправо 🙂
com _r_transmit_data
ldi _r_WAIT, 10
DATA_transmit_RS232_DATA_BITS:
sbrc _r_transmit_data, 0
cbi PORTB, _PIN_PC_OUT
sbrs _r_transmit_data, 0
sbi PORTB, _PIN_PC_OUT
rcall PAUSE_RS232
nop
nop
nop
nop
lsr _r_transmit_data
dec _r_WAIT
brne DATA_transmit_RS232_DATA_BITS
; по хорошему, надо было запомнить флаг прерывания в начале процедуры
; и вернуть его такой же в конце. Но мы не будем возвращать
; — по умолчанию живем без прерываний, если надо — выставим когда надо
;sei
ret
;———————————————————————
VALUE_transmit_RS232:
;_r_t1_hb:_r_t1_lb — на комп по RS232 в десятичной системе с запятой
;_r_count_measure_time_hb:_r_count_measure_time_lb — _SI_order
;_r_transmit_data — который потом шлем на комп по RS232
ldi _r_count_measure_time_lb, low(_SI_order_4)
ldi _r_count_measure_time_hb, high(_SI_order_4)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_3)
ldi _r_count_measure_time_hb, high(_SI_order_3)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_2)
ldi _r_count_measure_time_hb, high(_SI_order_2)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
; запятая
ldi _r_transmit_data, KOI_8_comma
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_1)
ldi _r_count_measure_time_hb, high(_SI_order_1)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_0)
ldi _r_count_measure_time_hb, high(_SI_order_0)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ret
;———————————————————————
MEASURE_transmit_RS232:
; вынес в процедуру ИСКЛЮЧИТЕЛЬНО из-за ограничения количества строк перехода!
;_r_t1_hb:_r_t1_lb — на комп по RS232 в десятичной системе с запятой
;_r_count_measure_time_hb:_r_count_measure_time_lb — _SI_order
;_r_transmit_data — который потом шлем на комп по RS232
; сначала рассчитаем и приготовим все нужные рагистры для передачи
; а потом уже передадим — при передаче они корежатся 🙂
; расчет и запись ti_last как разница ti_lastт — ti
cp _r_t1_last_lb, _r_t1_lb
cpc _r_t1_last_hb, _r_t1_hb
brlo _clr_r_t1_last
sub _r_t1_last_lb, _r_t1_lb
sbc _r_t1_last_hb, _r_t1_hb
rjmp _calc_r_t1_last_end
_clr_r_t1_last:
clr _r_t1_last_lb
clr _r_t1_last_hb
_calc_r_t1_last_end:
cp _r_t2_last_lb, _r_t2_lb
cpc _r_t2_last_hb, _r_t2_hb
brlo _clr_r_t2_last
sub _r_t2_last_lb, _r_t2_lb
sbc _r_t2_last_hb, _r_t2_hb
rjmp _calc_r_t2_last_end
_clr_r_t2_last:
clr _r_t2_last_lb
clr _r_t2_last_hb
_calc_r_t2_last_end:
; тут в _r_t1_hb:_r_t1_lb уже нужное число для передачи — ничего не делаем
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ «;»
mov _r_t1_lb, _r_t1_last_lb
mov _r_t1_hb, _r_t1_last_hb
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ «;»
mov _r_t1_lb, _r_t2_lb
mov _r_t1_hb, _r_t2_hb
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ «;»
mov _r_t1_lb, _r_t2_last_lb
mov _r_t1_hb, _r_t2_last_hb
rcall VALUE_transmit_RS232
; счетчик измерений считает от 0 вверх
; если он четный — в конце передаем «;», иначе переход на новую строку
sbrc _r_count_measure_sets, 0
rjmp MEASURE_transmit_RS232_SEND_TO_PC_new_line
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ «;»
rjmp MEASURE_transmit_RS232_END
MEASURE_transmit_RS232_SEND_TO_PC_new_line:
ldi _r_transmit_data, KOI_8_ps
rcall DATA_transmit_RS232
ldi _r_transmit_data, KOI_8_vk
rcall DATA_transmit_RS232 ; переход на новую строку
MEASURE_transmit_RS232_END:
ret
;———————————————————————
INITIALIZE_MEASURE:
; вынес в процедуру ИСКЛЮЧИТЕЛЬНО из-за ограничения количества строк перехода!
clr _r_count_measure_time_lb
clr _r_count_measure_time_hb
ser _r_t1_lb
ser _r_t1_hb
ser _r_t2_lb
ser _r_t2_hb
; используем тут _r_transmit_data для начального состояния входов
in _r_transmit_data, PINB
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0) + (1<<SM1)
sbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; задаем режим sleep-а Idle и устанавливаем флаг его разрешения
ldi _r_mom, _one_measure_time — 1 — 8 — 4 — 8
out OCR0A, _r_mom ; значение счетчика таймера для прерывания
; 1 — на установку флага прерывания
; 8 — на переход на адрес вектора прерывания
; 4 — на reti с адреса вектора прерывания
; 8 = 2 на сброс счетчика таймера + 6 на проверку условия цикла
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_mom, (1<<OCIE0A)
out TIMSK0, _r_mom ; разрешаем прерывание по счетчику таймера
ldi _r_mom, (0<<CS02) + (0<<CS01) + (1<<CS00)
out TCCR0B, _r_mom ; запускаем таймер с частотой СК
sei
ret
;———————————————————————
MAIN:
cbi PORTB, _PIN_RELE_OUT1
cbi PORTB, _PIN_RELE_OUT2
sbi PORTB, _PIN_PC_OUT ; инициализируем передачу на комп
; ldi _r_mom, (1<<PCIE)
; out GIMSK, _r_mom ; разрешаем прерыванияпо изменению состояния выводов
; in _r_mom, MCUCR
; cbr _r_mom, (1<<SM0)
; sbr _r_mom, (1<<SM1) + (1<<SE)
; ;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
; out MCUCR, _r_mom ; задаем режим sleep-а Power-down и устанавливаем флаг его разрешения
; sei
; sleep ; Power-down
; тут проснемся от прерывания PIN_CHANGE
ldi _r_WAIT, 20 ; 37.5 Гц
rcall WAIT_Idle ; ждать 1s
; инициализировать контакты реле
sbi PORTB, _PIN_RELE_OUT2
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
cbi PORTB, _PIN_RELE_OUT2
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; провести серию измерений, записывая результаты каждого в EEPROM
clr _r_count_measure_sets
SET_OF_MEASURES:
rcall INITIALIZE_MEASURE
; команда на переключение — инвертируем _PIN_RELE_OUT1
ldi _r_WAIT, (1<<_PIN_RELE_OUT1)
in _r_mom, PORTB
eor _r_mom, _r_WAIT
out PORTB, _r_mom
; измеряем t1 и t2
MEASURE: ; опрос пинов строго раз в 10us = 96 тактов
; таких опросов строго до достижения _max_measure_time
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
; тут у нас есть на все наши операции максимум 96 — 1 — 8 — 4 — 8 = 75 тактов!
; ldi _r_mom, 25
;a3: dec _r_mom
; brne a3
; ; 75 тактов
; ;nop
; ;nop
; nop
ldi _r_mom, 1
clr _r_WAIT
add _r_count_measure_time_lb, _r_mom
adc _r_count_measure_time_hb, _r_WAIT ; инкремент счетчика
; манипуляции с _r_mom, после которых в битах _PIN_RELE_IN1 и _PIN_RELE_IN2
; будут 1-цы только если текущие значения не равны начальным из _r_transmit_data
in _r_mom, PINB
eor _r_mom, _r_transmit_data
; старшие байты _r_t1_hb и _r_t2_hb изначально забиты 1-цами
; а после занесения в них значений измерения — гарантированно меньше,
; т.к. максимальное значение у нас будет 3000 а возможное — 65536
cpi _r_t1_hb, 255
brne MEASURE_t1_END ; пропускаем если _r_t1_hb <> 255 — уже заполнили
sbrs _r_mom, _PIN_RELE_IN1
rjmp MEASURE_t1_END ; пропускаем если состояние не изменилось
mov _r_t1_lb, _r_count_measure_time_lb
mov _r_t1_hb, _r_count_measure_time_hb
MEASURE_t1_END:
cpi _r_t2_hb, 255
brne MEASURE_t2_END ; пропускаем если _r_t2_hb <> 255 — уже заполнили
sbrs _r_mom, _PIN_RELE_IN2
rjmp MEASURE_t2_END ; пропускаем если состояние не изменилось
mov _r_t2_lb, _r_count_measure_time_lb
mov _r_t2_hb, _r_count_measure_time_hb
MEASURE_t2_END:
; каждый цикл записываем тек. время если состояние вывода РАВНО исходному
; — так отследим время окончания дребезга
sbrc _r_mom, _PIN_RELE_IN1
rjmp MEASURE_t1_last_END ; пропускаем если состояние изменилось
mov _r_t1_last_lb, _r_count_measure_time_lb
mov _r_t1_last_hb, _r_count_measure_time_hb
MEASURE_t1_last_END:
sbrc _r_mom, _PIN_RELE_IN2
rjmp MEASURE_t2_last_END ; пропускаем если состояние изменилось
mov _r_t2_last_lb, _r_count_measure_time_lb
mov _r_t2_last_hb, _r_count_measure_time_hb
MEASURE_t2_last_END:
; заняли максимум 40 тактов
sleep
ldi _r_mom, low(_max_measure_time)
ldi _r_WAIT, high(_max_measure_time)
cp _r_count_measure_time_lb, _r_mom
cpc _r_count_measure_time_hb, _r_WAIT
brlo MEASURE
; прошли _max_measure_time — сделали очередное измерение
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; сбрасываем флаг разрешения sleep-а
clr _r_mom
out TCCR0B, _r_mom ; останавливаем таймер
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; команда на исходное положение — инвертируем _PIN_RELE_OUT2
ldi _r_WAIT, (1<<_PIN_RELE_OUT2)
in _r_mom, PORTB
eor _r_mom, _r_WAIT
out PORTB, _r_mom
; передать результаты на комп
rcall MEASURE_transmit_RS232
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; если ещё не все измерения сделаны (а одно измерение — это сначала туда а потом обратно)
; то сделаем ещё измерение
inc _r_count_measure_sets
cpi _r_count_measure_sets, 2*_sets_of_measures
brlo SET_OF_MEASURES
; провели серию измерений и передали результаты на комп —
; готовы к новой серии измерений (на старт и PowerDown)
;rjmp MAIN
; нет — тут падаем в Power-down без права прерывания и ждем RESET :)))
SLEEP_Power_down:
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0)
sbr _r_mom, (1<<SM1) + (1<<SE)
;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
out MCUCR, _r_mom ; задаем режим sleep-а Power-down и устанавливаем флаг его разрешения
sleep ; Power-down
rjmp SLEEP_Power_down
dxdy.ru
Решение: Вывод на экран содержимого регистра bx в 16-ричном виде
Добрый вечер всем. Имеется код подсчета двоичных нулей в переменной размером двойное слово. Не могу вывести на экран количество нулей, хранящихся в регистре bx. Вывести нужно в 16-тиричном виде. Пробовала статейку (Ввод и вывод чисел в различных системах счисления) — результата нет. Заранее спасибо за помощь и пояснения..model small .stack 100h .data mes1 DB 'Zadannoe 4islo:', 13, 10, '$' mes2 DB '', 13, 10, '$' mes3 DB 'Kolichestvo nylei:', 13, 10, '$' PER dd 1110101000100010b KOL equ 1 .code Start: MOV AX, @DATA MOV DS, AX xor AX, AX xor BX, BX mov AH, 09h ; выводить mov DX, OFFSET mes1 ; сообщение int 21h ; на экран lea si,PER ; загружаем в si адрес нашего двоичного числа mov cx, KOL ; пересылаем в регистр cx количество элементов call ShowArray ; вызов процедуры вывода на экран mov dx,[si] ; пересылаем в dx значение элемента mov cx,16 ; пересылаем в cx количество битов в элементах met: rcl dx,1 JNC met_1 met_2: loop met mov AH, 09h ; выводить mov DX, OFFSET mes2 ; сообщение int 21h ; на экран mov DX, OFFSET mes2 int 21h mov DX, OFFSET mes3 int 21h JMP met_3 met_1: inc bl JMP met_2 met_3: mov AX, 4C00h int 21h ShowArray proc @@for: mov ax, [si] ; пересылаем в регистр ax, значение массива call ShowWord ; вызов процедуры mov al, ' ' ; пробел между элементами int 29h ; add si, 2 ; + loop @@for ; вычитает единицу из CX, если больше нуля то ; переход на указанную метку ret ; вернуться из подпрограммы ShowArray endp ;-------------------- ShowWord proc push ax ; загружаем ax в стек (значение в ячейку памяти) push dx ; загружаем dx в стек push cx ; загружаем cx в стек mov cx,16 ; пересылаем в cx, количество битов mov dx, ax ; пересылаем в регистр dx @@ForEachBit: mov al, '0' ; пересылка в al, символьную строку shl dx, 1 ; сдвиг dx влево, кол-во сдвигов adc al, 0 ; сложение двух операндов, прибавляя к ним значение cx int 29h loop @@ForEachBit ; переход к метке, если больше 0 pop cx ; выгружаем cx из стека pop dx ; выгружаем dx из стека pop ax ; выгружаем ax из стека ret ; вернуться из подпрограммы ShowWord endp END Start
studassistent.ru