Разное

Ассемблер вывод числа на экран – Учебный курс. Часть 22. Вывод чисел на консоль

Учебный курс. Часть 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

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

Главная / Ассемблер / Примеры (исходники) /
Как выучить английский

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

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

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

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

  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
;==========================================================

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



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

Отправить ответ

avatar
  Подписаться  
Уведомление о