Как вывести символ в ассемблере
Как вывести символ в ассемблере
Как уже отмечалось ранее, микропроцессор оперирует двоичными числами. Но на экране компьютера мы видим привычные символы: цифры, буквы и знаки. Данная тема раскроет секреты вывода на экран отдельных символов и строк.
Знакомство с прерываниями
Прерывание INT 21h выполняет функцию, заданную в регистре AH. Подпрограмма функции содержит значительное количество инструкций, поэтому трассировка прерывания может завести нас очень далеко. Для выполнения прерывания используем команду «G» (Go), которая выполняет все инструкции подряд. Адрес остановки (т.е. адрес инструкции, следующей за INT 21h) мы укажем непосредственно в команде G:
Операционная система вывела букву «A» и передала управление Debug. По причинам рассмотренным ранее, последняя строка вашего листинга будет выглядеть иначе.
Используя инструкцию INT 21h, выведите следующие символы на экран:
В качестве эксперимента попробуйте трассировать прерывание. Завершить трассировочный «поход» по инструкциям прерывания можно командой «G 102». Аналогичный эксперимент проделайте с командой G, не указывая в ней точку остановки.
Выход из программы
Введите коды [CDh, 20h] по адресам 100h и 101h. Проверьте наличие инструкции INT 20h командой «R», и выполните прерывание командой «G»:
Прерывание INT 20h сообщает об успешном выполнении программы, и передает управление Debug. Кроме того, INT 20h восстанавливает содержимое всех регистров к виду, в котором они были до запуска программы. То есть, после выполнения INT 20h регистр IP вернется в исходное состояние: IP = 100h.
Инструкцию INT 20h логично добавлять в конец каждой программы. Например, объединим INT 21h и INT 20h в одну программу, которая будет печатать заданный символ на экран:
Курсивом помечены инструкции, оставшиеся от программ или данных, отработавших до запуска Debug. С увеличением сложности примеров, размер дизассемблированного листинга также будет увеличиваться.
Занесите в регистр AH функцию 02h, а в регистр DL код символа «#». Запустите программу: Проверьте работу программы с другими символами. Таблица ASCII кодов
Ввод программы
До сих пор мы вводили коды инструкций, например: [CDh, 21h]. Но это очень неудобно. В Debug имеется команда «A» (Assemble), позволяющая вводить мнемокоды инструкций:
Параметр 100 означает, что ввод инструкций начнется с адреса 100h.
Команда MOV
Команда «R» позволяет изменять значения регистров до и после запуска программы. В ходе программы, число в регистр можно загрузить командой MOV. Например, загрузим функцию печати числа в регистр AH следующим образом:
Проверьте наличие команды в листинге регистров:
В исходном состоянии AX = 0000. Выполните трассировку, и вновь просмотрите регистры. Теперь AX = 0200 (AH = 02h).
Команда MOV позволяет копировать числа из регистра в регистр. Например, скопируем число из регистра BX в регистр AX:
Загрузите в регистры числа: AX = FF00, BX = 00EE. Выполните трассировку команды. После просмотра регистров вы увидите, что AX = 00EE. Содержимое регистра BX не изменилось.
Следующая программа демонстрирует возможности команды MOV: Выполните трассировку, анализируя каждый шаг программы.
Используем команду MOV, для модернизации программы вывода символа на экран:
Программа будет печатать символ «*» (ASCII код 2Ah). Используйте команду «U 100» для проверки введенных инструкций. Убедитесь, что регистр IP = 100h. Выполните программу:
Запись программы на диск
В предыдущем разделе была рассмотрена программа вывода на экран символа «*». Чтобы записать эту программу на диск, следует выполнить три действия:
Если программа вывода звездочки уже потеряна, то обратитесь к предыдущему разделу для ее повторного ввода.
Используя команду «N» (Name), присвойте будущему файлу имя:
Для записи программы на диск используйте команду «W» (Write):
Если вы используете FAR, то программа будет записана на диск в текущий каталог. Покиньте Debug и запустите программу Star.com. В приложении FAR, результат выполнения Star.com будет скрыт панелями. Включить/выключить панели FAR можно комбинацией: [Ctrl] + [O].
Вывод на экран строки символов
Как отмечалось ранее, прерывание INT 21h может выполнять несколько различных функций. Мы рассмотрели функцию 02h, которая выводит на экран отдельные символы. Для вывода на экран строк, используется функция 09h. Например, требуется вывести на экран строку:
Список ASCII кодов для этой строки следующий:
Начиная с адреса 109h, запишите в память коды строки. Переход от одной ячейки памяти к другой выполняйте клавишей [пробел]:
Начиная с адреса 100h, введите в память программу: В регистр DX заносится адрес первого символа строки (указатель на строку).
Дизассемблируйте коды программы:
Установите регистр IP = 100h, и запустите программу: Программа готова к записи на диск. Присвойте программе имя:
Покиньте Debug, и проверьте работу программы Champion.com.
Редактирование строк
Использовать команду «E», для ввода или редактирования строки, очень неудобно. Быстро отредактировать строку в COM-файле позволяет текстовый редактор, поддерживающий DOS кодировку. Например, в FAR: ставим курсор на файл Champion.com и жмём клавишу [F4]. В результате откроется окно редактора с символьным представлением кодов программы:
Как вывести символ в ассемблере
На примере первой программы на ассемблере мы уже познакомились с одним из способов вывода текста на экран — вызовом функции 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 символов в строке.
Это программа типа СОМ, и компилироваться она должна точно так же, как 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.out, содержащий строку «Hello World!». Точно так же, если написать
то в файле 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
Действие этой функции полностью аналогично действию функции 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.out окажется пустым.
И наконец, последняя функция DOS вывода на экран — недокументированное прерывание 29h.
INT 29h : Быстрый вывод символа на экран
Ввод: | AL = ASCII-код символа |
В большинстве случаев INT 29h просто немедленно вызывает функцию BIOS «вывод символа на экран в режиме телетайпа», так что никаких преимуществ, кроме экономии байт при написании как можно более коротких программ, она не имеет.
Графический ввод-вывод на языке ассемблер
Графика в жизни программиста занимает особое место. В различных программах графика уже реализована в большей или меньшей степени. Начинающие программисты думают, что вне зависимости от языка программирования, графика реализуется одинаково. Но работа с графикой в каждом языке уникальна. В данной статье мы рассмотрим графическую составляющую языка ассемблер.
Операции ввода/вывода по прерыванию INT 21H
Уникальность ассемблера состоит в том, что он напрямую указывает процессору режим работы с графикой. В ассемблере существует множество режимов, которые определяются шестнадцатеричным числом, содержащимся в регистре АH. Рассмотрим эти примеры прерываний.
AH=01: Ввод с клавиатуры с отображением в консоли. Данная операция считывает с консолисимвол и помещает его в регистр AL. После считывания содержимое данного регистра представляет собой ASCII-символ. Если после считывания символа регистр AL будет равен 0, значит, с клавиатуры были нажаты специальные клавиши (F1, PgUp и т.д). Для определения их кода необходимо повторить запрос.
AH=02: Вывод символа в консоль. При выводе символа на экран в текущую позицию курсора необходимо поместить код данного символа в регистр DL.
AH=06: Ввод/вывод символов. Данный режим может использоваться, как для ввода, так и для вывода. Для вывода в консоль символа необходимо поместить его в DL и вызвать прерывание INT 21H. Для ввода символа необходимо занести в DL FFh и вызвать прерывание INT 21H. При использовании данной команды программа не остановиться, а продолжит выполняться в штатном режиме. При вводе символ поместиться в регистр AL.
AH=07:Ввод с клавиатуры без отображения в консоли. Данная команда работает аналогично с командой при AH=01H, но немного отличается. При вводе символа с клавиатуры он не отображается на экране.
AH=09H: Вывод строки в консоль. Для вывода строки необходимо, чтобы она заканчивалась специальным символом ($). Затем адрес начала строки помещается в регистр DX. Символ доллара в консоли не выводится.
Пример кода выполнения операции прерывания при AH=09H:
Leadx, str; смещение регистра DX до начала строки
Movah, 9; Помещаем в ah 9 для вывода строки
Int 21h; Прерывание для вывода строки
AH=0AH: Ввод значений в буфер.При данном режиме работы данные помещаются в буфер, где определена максимальная длина введенной строки. Если при вводе, строка оказывается меньше в длинны буфера, то окончание ввода строки определяется символом клавиши [ENTER], если больше длины буфера, тогда пользователь предупреждается звуковым сигналом. При переполнении буфера введенная строка не учитывается. Во второй байт буфера возвращается реальная длинна введенной строки в байтах. Адрес буфера заносится в регистр DX. Таким образом, в первом байте находится максимальная длина вводимой строки, так как это поле является однобайтовым, значит максимальная длина будет равна 255 символов (FF- шестнадцатеричное представление). Во втором байте храниться реальное количество символов. В третьем байте хранятся введенные символы.
Ввод /вывод по прерыванию int 10H
Для вывода символов в консоль или их ввода используются различные команды DOS. Однако он не поддерживает смену цвета в консоли ни перемещение курсора, но возможности DOS можно увеличить при помощи драйвера ANSI.SYS. С графической составляющей всё гораздо хуже в DOS нет никаких графических команд. Зато можно переключить видеоадаптер в графический режим с помощью Esc-последовательностей. Для осуществления перехода необходимо обратиться к нижним уровням операционной системы, называемой BIOS (BasicIn-OutSystem). Программы Bios находятся в ПЗУ(постоянное запоминающее устройство). BIOS в отличии от DOS (все команды выполняются при помощи инструкций прерываний int 21h), выполнение команд осуществляется при помощи различный команд прерываний, то есть за каждым устройством закреплён определённая команда прерываний (клавиатура- int 16h, экран-int 10h, и так далее). Прерывание int 10h выполняет все команды видеоадаптера: вывод строк и символьной информации, смена шрифтов, смена цвета, работа с изображениями и т.д.
Пример: Вывод на экран горизонтальной прямой
;Устанавливаем графический режим
mov ah,00h ;(1)Команда для задания режима
mov al,10h ;(2)Входим в графический режим
int 10h ;(3)Вызов BIOS
mov si,150 ;(4)si- Начальная координата (X)
mov cx,300 ;(5)Количество точек по горизонтали
pushcx ;(6)Помещаем количество точек в стек
mov ax,0Ch ;(7)Команда вывода пикселя
mov al,4 ;(8)Устанавливаем цвет красный
mov bh,0 ;(9)Видеостраница
movcx,si ;(10)Х-координата (изменяется)
mov dx,175 ;(11)Y-координата (постоянная)
int 10h ;(12)Вызов BIOS
incsi ;(13)Инкремент Х-координаты
popcx ;(14)Восстановим количество шагов
loopliniya ;(15)Цикл из CX шагов
;Останавливаем программу для просмотра её работы
mov ah,08h ;(16)Команда ввода с клавиатуры без отображения в консоли
int 21h ;(17)Вызов DOS
;Переключаем видеоадаптер в текстовый режим
mov ah,00h ;(18)Команда задания режима
mov al,03h ;(19)Текстовый режим
int 10h ;(20)Вызов BIOS
В 1-3 строчках при помощи команды 00H вызвали прерывание Bios 10h и осуществили переключение видеоадаптера в режим графики. Затем поместили номер режима в регистр AL, всего существует порядка 256 разных режимов (текстовых и графических), однако на сегодняшний день используется порядка ста режимов. Команда прерывания int 10h обеспечивает графическое отображение с разрешением 640х350 пикселей и около 16 цветов.
Все изображения рисуются по точкам (пиксели) так как в BIOS нет команд для вывода каких либо геометрических фигур и нет команд закрашивающих области. Для вывода цветного пикселя на экран используется команда 0CH и прерывание int 10h. Данная команда необходима для занесения в регистр AL кода цвета, в BH- номера видеостраницы, в CX – координаты точки по оси Х в, в DX –координаты точки впо оси Y, все координаты вводятся в диапазоне от 0 до 639.
Рассмотрим параметры вызова команд 0Ch прерывания int 10h. Заносим номер видеостраницы, куда выводится данная точка, в регистр BH. А регистр AL заносим код цвета выводимого пикселя. Данный адаптер содержит 16 цветов. Данный набор из 16 цветов задаётся программно и может изменятся в любой момент выполнения программы.
Подведём итог графической работы BIOS и прерывания int 10h. Для вывода на экран изображений используется графический режим. Через прерывание int 10h устанавливается текстовый или графический режим. Для установки режима AH обнуляется и в AL заносится код режима работы, а затем выполняется прерывание 10H.
В данной работе мы рассмотрели режимы работы графической составляющей языка ассемблер. И сделали вывод, что все образы и картинки состоят из точек(пикселей),а значит можно нарисовать любой графический объект, меняя координаты вывода следующего пикселя.
Вывод текста в окно на ассемблере
По описанным выше причинам, экран в Windows это уже среда, разделенная между разнообразными «одновременно» функционирующими графическими приложениями. Логично предположить, что в многозадачных операционных системах должны действовать более строгие правила, дабы исключить ситуации, когда приложения используют для вывода своей информации чужую область экрана (окно/часть окна стороннего приложения). Поэтому, вывод текста в окно графического интерфейса Windows далеко не такая тривиальная задача, как её аналог для текстового режима в MSDOS, и тут нам необходимо усвоить ряд правил:
Области (регионы)
Фактически объектами любого современного графического интерфейса являются окна, представляющие собой прямоугольные области произвольного размера. Пользовательское приложение, как и система, оперирует понятием окна как некой прямоугольной областью, ограничивающей вывод информации. Окно в Windows разделено на несколько областей:
Клиентская область это фактически регион окна приложения, за обслуживание которого отвечает само приложение. За неклиентский регион (non-client area), бордюры, заголовок, ползунок, окна, отвечает ядро системы.
Дабы приложения не «портили» окна чужих приложений, по умолчанию Windows обеспечивает контроль за выводом посредством ограничения области отрисовки каждого окна только его собственной клиентской областью.
Надо учитывать, что размер этой клиентской области окна не постоянен, поскольку пользовательская активность является причиной постоянных изменений атрибутов окна (размер, видимость и прч.). Вот именно это обстоятельство и является ключевым в понимании природы графического интерфейса. Теперь давайте поговорим о действительных и недействительных областях.
(Не)действительные области
В какой-то момент времени изображение (содержимое) окон всех приложений переносится системой в память видеоадаптера (видеопамять) для непосредственного отображения на экране. Начиная с этого момента подразумевается, что системе известно содержимое части экрана, соответствующее всем окнам, контекст которых был только что перенесен. Ядро постоянно отслеживает объекты графического интерфейса, расположенные на экране: изменения размеров окон, их перемещение. Если никаких изменений в окнах приложений не происходит, то видеоадаптер обновляет содержимое экрана, периодически выбирая его из собственной видеопамяти. Если окна приложений начинают открываться/закрываться/перемещаться, то на экране появляются области, информация об изображении которых теряет актуальность, иными словами потеряна (неизвестна) для операционной системы. В этом случае система извещает окна, области (части, фрагменты) которых стали недействительными, о необходимости восстановить (перерисовать) собственное содержимое. Ведь на рабочем столе зачастую возникают ситуации, когда пользователь перетаскивает существующее стороннее окно либо поверх содержимого нашего окна, затем закрывает/вновь перемещает его, или просто сворачивает и разворачивает окно приложения. Поскольку часть окна (или окно целиком) перекрыта содержимым находящегося выше окна, оригинальная часть замещена (скрыта) частью содержимого перекрывающего окна, поэтому часть клиентская области перекрытого окна становится недействительной. Давайте перечислим виды областей:
Если честно, перед начинающим программистом встают не такие уж и тривиальные задачи. Тут, как говорится, пока не пощупаешь все эти подходы на практике, принципа не поймешь. К примеру, если мы пишем эдакий простенький графический редактор, который предназначен для рисования мышью (абстрактная кривая) в основном окне, мы должны хранить все точки данной фигуры, потому как в случае перемещения окна нам потребуется отрисовать (восстановить) её всю целиком. Та же ситуация и с текстом, приложение должно хранить всю ту информацию, которая использовалась во всех элементах окна и на основании которой оно может в любое время окно обновить. При работе с текстовой информацией (выводе/вводе), код приложения должен сохранять данные в памяти, чтобы их можно было «по требованию» системы вывести на экран, тем самым обновив (перерисовав) актуальное содержимое окна. Таким образом, принимая во внимание всё вышеизложенное, можно сделать следующий вывод:
Контекст устройства [отображения]
Очевидно, что отрисовку можно производить как внутри кода обработки любого входящего сообщения (чаще всего WM_PAINT ), так и вне процедуры обработки сообщений окна, например в процедуре обработки сообщений потока. Но, не все так просто как хотелось бы, и для этого, что бы начать «рисовать», требуется еще поизвести ряд подготовительных действий. В коде процедуры обработки сообщения, непосредственно перед началом рисования чего-либо в клиентской области «собственного» окна, приложению необходимо запросить у системы так называемое «разрешение» на подобные действия. Под разрешением подразумевается системная структура под названием контекст устройства отображения (контекст отображения). Советую перейти по приведенной ссылке и ознакомиться с материалом. После этого, пожалуй, продолжим.
Методы получения контекста устройства
Как уже упоминалось, для начала работы с контекстом устройства, требуется получить его описатель у операционной системы. Говоря простым языком, каждый раз, когда приложению надо что-то нарисовать, код при помощи специализированных функций должен запросить и получить дескриптор контекста устройства. А вот тут то начинается определенная конкретика. Дело в том, что методы запроса контекста устройства различаются в зависимости от условий и целей, при которых этот запрос выполняется:
Как вывести строку на экран в Ассемблере
Что бы ни делала ваша программа, в большинстве случаев она должна выводить какие-то данные на экран. И если в языках высокого уровня это делается “лёгким движением руки”, то в ассемблере для этого приходится как следует помучиться.
Правда, в современных воплощениях языка Ассемблера могут быть стандартные макросы или подпрограммы для вывода строк на экран. Однако начинающим будет полезно разобраться с тем, как это можно сделать только с помощью инструкций, без применения каких-либо облегчающих жизнь библиотек.
Итак, выводить строки на экран можно двумя путями:
Кроме того, в текстовом режиме вывод на экран можно выполнить одним из трёх способов:
Третий способ хорош тем, что он сразу записывает данные в видеопамять, что позволяет выполнять вывод более быстро. Однако в наше время он применим, разве что, в учебных целях. Потому что современные операционные системы не позволяют напрямую обращаться к “железу”.
Поэтому, если ваша программа является чуть более, чем учебной, то придётся применять один из первых двух способов. Ну а если вам интересно, как выполнить вывод непосредственно в видеопамять, то посмотреть примеры вы можете здесь или здесь.
Функции вывода DOS
Итак, начнём с функций вывода DOS. Эти функции являются функциями операционной системы DOS, но поддерживаются и операционными системами Windows.
Как я уже говорил, можно напечатать на экране строку в цикле, отдельно выводя каждый символ. Для этих целей можно использовать функции 02h, 06h или недокуметированное прерывание 29h.
Если требуется вывести на экран строку целиком, то можно применить функции 09h и 40h.
Для использования функций DOS надо сначала подготовить необходимые данные, записать номер функции в регистр AH, а затем вызвать прерывание 21h.
При правильном использовании на экран будет выведено примерно следующее:
Функции вывода BIOS
Функции BIOS также могут выводить как отдельные символы (функции 09h, 0Ah, 0Eh), так и строки целиком (функция 13h).
Кроме того с помощью функций BIOS можно установить видеорежим, установить или считать положение курсора, а также считать символ и его атрибуты.
Хотя функции DOS тоже могут считывать символы, но всё-таки возможности BIOS более широки.
Для работы с функциями BIOS также сначала надо подготовить данные, записать номер функции в регистр AH, а затем вызвать прерывание 10h.
Для примера рассмотрим функцию 13h. Перед вызовом функции надо:
Рассказывать об остальных функциях, а также об установке атрибутов и прочих вещах сегодня не буду. Если кому интересно, то всё это можно найти в справочных материалах.