Assembler - язык неограниченных возможностей

https://www.ecolider.ru реагент бионорд цена.              

Средства DOS


На примере первой программы на ассемблере мы уже познакомились с одним из способов вывода текста на экран — вызовом функции DOS 09h. Это далеко не единственный способ вывода текста — DOS предоставляет для этого несколько функций.

Функция DOS 02h — Записать символ в STDOUT с проверкой на Ctrl-Break

Ввод: АН = 02h
DL = ASCII-код символа
Вывод: Никакого, согласно документации, но на самом деле: AL = код последнего записанного символа (равен DL, кроме случая, когда DL = 09h (табуляция), тогда в AL возвращается 20h).

Эта функция при выводе на экран обрабатывает некоторые управляющие символы — вывод символа BEL (07h) приводит к звуковому сигналу, символ BS (08h) приводит к движению курсора влево на одну позицию, символ НТ (09h) заменяется на несколько пробелов, символ LF (0Ah) опускает курсор на одну позицию вниз, и CR (0Dh) приводит к переходу на начало текущей строки.



Если в ходе работы этой функции была нажата комбинация клавиш Ctrl-Break, вызывается прерывание 23h, которое по умолчанию осуществляет выход из программы.

Например, напишем программу, выводящую на экран все ASCII-символы, 16 строк по 16 символов в строке.

; dosoutl.asm ; Выводит на экран все ASCII-символы ; .model tiny .code org 100h ; начало СОМ-файла start: mov ex,256 ; вывести 256 символов mov dl,0 ; первый символ - с кодом 00 mov ah,2 ; номер функции DOS "вывод символа" cloop: int 21h ; вызов DOS inc dl ; увеличение DL на 1 - следующий символ test dl,0Fh ; если DL не кратен 16, jnz continue_loop ; продолжить цикл, push dx ; иначе: сохранить текущий символ mov dl,0Dh ; вывести CR int 21h mov dl,0Ah ; вывести LF int 21h pop dx ; восстановить текущий символ continue_loop: loop cloop ; продолжить цикл ret ; завершение СОМ-файла end start

Это программа типа СОМ, и компилироваться она должна точно так же, как hello-1.asm в разделе 4.1. Здесь с помощью команды LOOP оформляется цикл, выполняющийся 256 раз (значение регистра СХ в начале цикла). Регистр DL содержит код символа, который равен нулю в начале цикла и увеличивается каждый раз на 1 командой INC DL. Если значение DL сразу после увеличения на 1 кратно 16, оно временно сохраняется в стеке и на экран выводятся символы CR и LF, выполняющие переход на начало новой строки. Проверка выполняется командой TEST DL,0Fh — результат операции AND над DL и 0Fh будет нулем, только если младшие четыре бита DL равны нулю, что и соответствует кратности шестнадцати.


Все функции DOS вывода на экран используют устройство STDOUT, стандартный вывод. Это позволяет перенаправлять вывод программы в файл или на стандартный ввод другой программы. Например, если написать в командной строке

hello-1.com > hello-1.out

то на экран ничего выдано не будет, а в текущем каталоге появится файл hello-1.out, содержащий строку «Hello World!». Точно так же, если написать

dosout1.com > dosout1.out

то в файле dosout1.out окажутся все символы ASCII, причем символы BEL и BS не будут интерпретироваться и запишутся в файл как есть. Символы CR и LF тоже запишутся как есть, но так как они отмечают конец строки, редакторы и просмотрщики текстовых файлов будут разрывать первую строку символов.

Функция DOS 06h — Записать символ в STDOUT без проверки на Ctrl-Break

Ввод: АН = 06h
DL = ASCII-код символа (кроме FFh)
Вывод: Никакого, согласно документации, но на самом деле: AL = код записанного символа (копия DL)
Эта функция не обрабатывает управляющие символы (CR, LF, HT и BS выполняют свои функции при выводе на экран, но сохраняются при перенаправлении вывода в файл) и не проверяет нажатие Ctrl-Break. Можно заменить в программе dosoutl.asm команду MOV АН,2 на MOV АН,6 и перекомпилировать этот пример, чтобы получить более полную таблицу символов.

Функция DOS 09h — Записать строку в STDOUT с проверкой на Ctrl-Break

Ввод: АН = 09h
DS:DX = адрес строки, заканчивающейся символом $ (24h)
Вывод: Никакого, согласно документации, но на самом деле: AL = 24h (код последнего символа)
Действие этой функции полностью аналогично действию функции 02h, но выводится не один символ, а целая строка, как в программах hello-1.asm и hello-2.asm.

Функция DOS 40h — Записать в файл или устройство

Ввод: АН = 40h
ВХ = 1 для STDOUT или 2 для STDERR
DS:DX = адрес начала строки
СХ = длина строки
Вывод: CF = 0,
АХ = число записанных байт
Эта функция предназначена для записи в файл, но, если в регистр ВХ поместить число 1, функция 40h будет выводить данные на STDOUT, а если ВХ = 2 — на устройство STDERR. STDERR всегда выводит данные на экран и не перенаправляется в файлы. На этой функции основаны используемые в С функции стандартного вывода — фактически функция С fputs() просто вызывает это прерывание, помещая свой первый аргумент в ВХ, адрес строки (второй аргумент) — в DS:DX и длину — в СХ.

; dosout2.asm ; Выводит на экран строку "This function can print $", ; используя вывод в STDERR, так что ее нельзя перенаправить в файл. .model tiny .code org 100h ; начало СОМ-файла start: mov ah,40h ; номер функции DOS mov bx,2 ; устройство STDERR mov dx,offset message ; DS:DX - адрес строки mov cx, message_length ; CX - длина строки int 21h ret ; завершение СОМ-файла message db "Эта функция может выводить знак $" message_length = $-message ; длина строки = текущий адрес ; минус адрес начала строки end start

Если скомпилировать эту программу и запустить ее командой

dosout2.com > dosout2.out

то сообщение появится на экране, а файл dosout2.out окажется пустым.

И наконец, последняя функция DOS вывода на экран — недокументированное прерывание 29h.

INT 29h: Быстрый вывод символа на экран

Ввод: AL = ASCII-код символа
В большинстве случаев INT 29h просто немедленно вызывает функцию BIOS «вывод символа на экран в режиме телетайпа», так что никаких преимуществ, кроме экономии байт при написании как можно более коротких программ, она не имеет.



/p> При чтении с помощью этой функции введенный символ автоматически немедленно отображается на экране (посылается в устройство STDOUT — так что его можно перенаправить в файл). При нажатии Ctrl-C или Ctrl-Break выполняется команда INT 23h. Если нажата клавиша, не соответствующая какому-нибудь символу (стрелки, функциональные клавиши Ins, Del и т.д.), то в AL возвращается 0 и функцию надо вызвать еще один раз, чтобы получить расширенный ASCII-код (см. приложение 1).

В трех следующих вариантах этой функции код символа возвращается в AL по такому же принципу.

Функция DOS 08h — Считать символ из STDIN без эха, с ожиданием и проверкой на Ctrl-Break

Ввод: АН = 08h
Вывод: AL = код символа
Функция DOS 07h — Считать символ из STDIN без эха, с ожиданием и без проверки на Ctrl-Break

Ввод: АН = 07h
Вывод: AL = код символа
Функция DOS 06h — Считать символ из STDIN без эха, без ожидания и без проверки на Ctrl-Break

Ввод: АН = 07h
DL = 0FFh
Вывод: ZF = 1, если не была нажата клавиша, и AL = 00
ZF = 0, если клавиша была нажата. В этом случае AL = код символа
Кроме перечисленных функций могут потребоваться и некоторые служебные функции DOS для работы с клавиатурой.

Функция DOS 0Bh — Проверить состояние клавиатуры

Ввод: АН = 0Bh
Вывод: AL = 0, если не была нажата клавиша
AL = 0FFh, если была нажата клавиша
Эту функцию удобно использовать перед функциями 01, 07 и 08, чтобы не ждать нажатия клавиши. Кроме того, вызов этой функции позволяет проверить, не считывая символ с клавиатуры, была ли нажата комбинация клавиш Ctrl-Break; если это произошло, выполнится прерывание 23h.

Функция DOS 0Ch — Очистить буфер и считать символ

Ввод: АН = 0Ch
AL = Номер функции DOS (01, 06, 07, 08, 0Ah)
Вывод: Зависит от вызванной функции
Функция 0Ch очищает буфер клавиатуры, так что следующая функция чтения символа будет ждать ввода с клавиатуры, а не использовать нажатый ранее и еще не обработанный символ. Например, именно эта функция используется для считывания ответа на вопрос «Уверен ли пользователь в том, что он хочет отформатировать диск?».



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

; dosin2.asm ; Изображает пентамино F, которое можно перемещать по экрану клавишами ; управления курсором и вращать клавишами X и Z. Выход из программы - Esc. ; line_length = 3 ; число символов в строке изображения number_of_lines = 3 ; число строк

.model tiny .code org 100h ; начало СОМ-файла start: cld ; будут использоваться команды ; строковой обработки mov ax,0B800h ; адрес начала текстовой видеопамяти mov es,ax ; в ES mov ax,0003h int 10h ; текстовый режим 03 (80x25) mov ah,02h ; установить курсор mov bh,0 mov dh,26 ; на строку 26, то есть за пределы экрана mov dl,1 int 10h ; теперь курсора на экране нет call update_screen ; вывести изображение

; основной цикл опроса клавиатуры main_loop: mov ah,08h ; считать символ с клавиатуры int 21h ; без эха, с ожиданием, с проверкой на Ctrl-Break test al,al ; если AL = 0 jz eASCII_entered ; введен символ расширенного ASCII cmp al,1Bh ; иначе: если введен символ 1Bh (Esc), je key_ESC ; выйти из программы, cmp al,'Z' ; если введен символ Z, je key_Z ; перейти на его обработчик cmp al,'z' ; то же для z je key_Z cmp al,'X' ; если введен символ X, je key_X ; перейти на его обработчик cmp al,'х' ; то же для х je key_X jmp short main_loop ; считать следующую клавишу

eASCII_entered: ; был введен расширенный ASCII-символ int 21h ; получить его код (повторный вызов функции) cmp al,48h ; стрелка вверх je key_UP cmp al,50h ; стрелка вниз je key_DOWN cmp al,4Bh ; стрелка влево je key_LEFT cmp al,4Dh ; стрелка вправо je key_RIGHT jmp short main_loop ; считать следующую клавишу ; ; обработчики нажатий клавиш ; key_ESC: ; Esc ret ; завершить СОМ-программу

key_UP: ; стрелка вверх cmp byte ptr start_row,0 ; если изображение на верхнем ; краю экрана, jna main_loop ; считать следующую клавишу, dec byte ptr start_row ; иначе - уменьшить номер строки, call update_screen ; вывести новое изображение jmp short main_loop ; и считать следующую клавишу



key_DOWN: ; стрелка вниз cmp byte ptr start_row,25-number_of_lines ; если ; изображение на нижнем краю экрана, jnb main_loop ; считать следующую клавишу, inc byte ptr start_row ; иначе - увеличить номер строки, call update_screen ; вывести новое изображение jmp short main_loop ; и считать следующую клавишу

key_LEFT: ; стрелка влево cmp byte ptr start_col,0 ; если изображение на левом краю ; экрана, jna main_loop ; считать следующую клавишу, dec byte ptr start_col ; иначе - уменьшить номер столбца, call update_screen ; вывести новое изображение jmp short main_loop ; и считать следующую клавишу

key_RIGHT: ; стрелка вправо cmp byte ptr start_col,80-line_length ; если ; изображение на правом краю экрана, jnb main_loop ; считать следующую клавишу, inc byte ptr start_col ; иначе - увеличить номер столбца, call update_screen ; вывести новое изображение jmp short main_loop ; и считать следующую клавишу

key_Z: ; клавиша Z (вращение влево) mov ax,current_screen ; считать номер текущего изображения ; (значения 0, 1, 2, 3), dec ax ; уменьшить его на 1, jns key_Z_ok ; если получился -1 (поменялся знак), mov ах,3 ; АХ = 3 key_Z_ok: mov current_screen,ax ; записать номер обратно, call update_screen ; вывести новое изображение jmp main_loop ; и считать следующую клавишу

key_X: ; клавиша X (вращение вправо) mov ax,current_screen ; считать номер текущего изображения ; (значения 0, 1, 2, 3), inc ax ; увеличить его на 1, cmp ax,4 ; если номер стал равен 4, jne key_X_ok xor ах,ах ; АХ = 0 key_X_ok: mov current_screen,ax ; записать номер обратно, call update_screen ; вывести новое изображение jmp main_loop ; и считать следующую клавишу

; процедура update_screen ; очищает экран и выводит текущее изображение ; модифицирует значения регистров АХ, ВХ, СХ, DX, SI, DI update_screen: mov cx,25*80 ; число символов на экране mov ax,0F20h; ; символ 20h (пробел) с атрибутом 0Fh ; (белый на черном) xor di,di ; ES:DI = начало видеопамяти rep stosw ; очистить экран mov bx,current_screen ; номер текущего изображения в ВХ shl bx,1 ; умножить на 2, так как screens - массив слов mov si,screens[bx] ; поместить в ВХ смещение начала ; текущего изображения из массива screens, mov ax,start_row ; вычислить адрес начала mul row_length ; изображения в видеопамяти add ax,start_col ; (строка * 80 + столбец) * 2 shl ax,1 mov di,ax ; ES:DI - начало изображения в видеопамяти mov ah,0Fh ; используемый атрибут - белый на черном mov dx,number_of_lines ; число строк в изображении сору_lines: mov cx,line_length ; число символов в строке copy_1: lodsb ; считать ASCII-код в AL, stosw ; записать его в видеопамять ; (AL - ASCII, АН - атрибут), loop copy_1 ; вывести так все символы в строке, add di,(80-line_length)*2 ; перевести DI на начало ; следующей строки экрана, dec dx ; если строки не закончились - jnz copy_lines ; вывести следующую ret ; конец процедуры update_screen



; изображение пентамино F screen1 db " XX" ; выводимое изображение db "XX " db " X "

screen2 db " X " ; поворот на 90 градусов вправо db "XXX" db " X"

screen3 db " X " ; поворот на 180 градусов db " XX" db "XX "

screen4 db "X " ; поворот на 90 градусов влево db "XXX" db " X " ; массив, содержащий адреса всех вариантов изображения screens dw screen1,screen2,screen3,screen4 current_screen dw 0 ; текущий вариант изображения start_row dw 10 ; текущая верхняя строка изображения start_col dw 37 ; текущий левый столбец row_length db 80 ; длина строки экрана для команды MUL

end start

В этом примере для вывода на экран используется прямое копирование в видеопамять, так как вызов функции BIOS вывода строки (INT 10h, АН = 13h) прокручивает экран вверх на одну строку при выводе символа в нижнем правом углу экрана.


Содержание раздела