Вывести сообщение на экран ассемблер

Обновлено: 30.06.2024

Для вывода данных на экран можно использовать два вида функций: универсальную функцию 40h (вывод в файл) и группу специализированных функций MS DOS вывода на экран.

Использование функции 40h уже рассматривалось в разделе, посвященном работе с файлами. Материал, представленный ниже, посвящен второй группе функций – функциям MS DOS для вывода символов на экран. В группу входят три функции. Рассмотрим их.

Вывод символа на экран (02h int 21h)

Функция 02h позволяет вывести один символ на экран.

Функция 02h проверяет наличие в клавиатурном буфере символов нажатия комбинации CTRL + C (CTRL + Break), при обнаружении которых производится вызов прерывания int 23h. В процессе вывода функция реагирует на управляющие символы, такие как 0dh (возврат каретки), 0ah (перевод строки), 08h (курсор назад на один символ), 07h (звуковой сигнал) и т. д.

Для того чтобы вывести строку, необходимо использовать цикл.

Прямой вывод символа на экран (06h int 21h)

Функция 06h выводит один символ на экран. Эта функция универсальна, так как используется и для ввода (см. выше), и для вывода символа.

Функция 06h не проверяет наличие в буфере символов нажатия комбинации CTRL + C (CTRL + Break). Порядок использования данной функции аналогичен порядку использования функции 02h.

Вывод строки на экран (09h int 21h)

  • DS: DX – адрес строки для вывода с завершающим символом $.

Функция 09h проверяет наличие в клавиатурном буфере символов нажатия комбинации CTRL + C (CTRL + Break), при обнаружении которых производится вызов прерывания int 23h. В процессе вывода функция реагирует на управляющие символы, такие как 0dh (возврат каретки), 0ah (перевод строки), 08h (курсор назад на один символ), 07h (звуковой сигнал) и т. д.

Для вывода на экран в DOS программах обычно используют функцию 9 системного сервиса DOS (прерывание 21h):

Ебать, в других языках чтобы вывести что-нить на экран нужно пару команд, это действие для новичка, что это за язык?

Миоко Таканава Гений (51313) А здесь что, намного больше? Всего три инструкции процессора. Только материться не стоит здесь.

честно говоря, мне ничего этого не известно. язык начал изучать третий день только.
поподробней, если можно.
У меня консольные приложения получаются и обращения к Dos идут. Как здесь сделать?

IRQ Оракул (51225) Я же говорю. Читай про видеопамять. Надо с помощью команд mov помойму просто записать по определённому адресу в память нужное значение. По какому адресу - сейчас не помню. Надо в книгах по асму смотреть. Ну и там же про запись в память. Обычно для этого сначала пишешь нужное значение в какой-либо регистр и далее из регистра пишешь по адресу в память.

Статья 1. Простейшая программа на языке ассемблера

Начнем изучение языка ассемблера с рассмотрения простой, возможно, даже наипростейшей программы (пример 1.1), которая выводит на экран терминала строку с текстом. Вопросы ввода в компьютер текста программы, ее трансляции и компоновки мы рассмотрим в следующей статье, а пока сосредоточимся на структуре программы.

Пример 1.1. Простейшая программа

Следует заметить, что при вводе исходного текста программы с клавиатуры можно использовать как прописные, так и строчные буквы: транслятор воспринимает, например, строки text segment и TEXT SEGMENT одинаково. Однако, с помощью ключа /ML можно заставить транслятор различать прописные и строчные буквы в именах. Тогда строки text segment и TEXT segment уже не будут эквивалентны. фактически они будут описывать два разных сегмента. Неэквивалентность прописных и строчных букв касается только имен; строки

во всех случаях воспринимаются одинаково.

В настоящей книге в программах и их описаниях мы используются преимущественно строчные буквы. прописными буквами выделены обозначения регистров и, иногда, имена программных и иных файлов.

Наша программа содержит 13 строк — предложений языка ассемблера. Первое предложение с помощью оператора segment открывает сегмент команд программы. Сегменту дается произвольное имя text . Описатель 'code' (так называемый класс сегмента) говорит о том, что это сегмент команд (слово code в переводе может означать и коды, и команды программы). В конце предложения после точки с запятой располагается комментарий. Таким образом, предложение языка ассемблера может состоять из четырех полей: имени, оператора, операндов и комментария, располагаемых в перечисленном порядке.

Любая программа должна обязательно состоять из сегментов — без сегментов программ не бывает. Обычно в программе задаются три сегмента: команд, данных и стека, но мы в нашей простой программе пока ограничились одним сегментом команд.

В предложении 2 мы с помощью оператора assume сообщаем ассемблеру (программе-транслятору), что сегментные регистры CS и DS будут указывать на один и тот же сегмент text . Сегментные регистры (а всего их в процессоре четыре) играют очень важную роль. Когда программа загружается в память и становится известно, по каким адресам памяти она располагается, в сегментные регистры заносятся начальные адреса закрепленных за ними сегментов. В дальнейшем любые обращения к ячейкам программы осуществляются путем указания сегмента, в котором находится интересующая нас ячейка, а также номера того байта внутри сегмента, к которому мы хотим обратиться. Этот номер носит название относительного адреса, или смещения. Поскольку в единственном сегменте нашей программы будут размещаться и команды, и данные, мы указываем ассемблеру оператором assume (assume — предположим), что и сегментный регистр команд CS, и сегментный регистр данных DS будут указывать на сегмент text . При этом в регистр CS адрес начала сегмента будет загружен автоматически, а регистр DS нам придется инициализировать вручную.

Строго говоря, в приведенной программе, где нет прямых обращений к ячейкам сегмента данных, не было необходимости сопоставлять в операторе assume сегмент text с сегментным регистром DS (сопоставление сегмента команд с сегментным регистром команд CS обязательно во всех случаях). Учитывая, однако, что практически в любой разумной программе обращения к полям данных имеются, мы с самого начала написали оператор assume в том виде, в каком он используется в реальных программах.

Первые два предложения программы служат для передачи служебной информации программе ассемблера. Ассемблер воспринимает и запоминает эту информацию и пользуется ею в своей дальнейшей работе, однако в состав выполнимой программы, состоящей из машинных кодов, эти строки не попадут, так как процессору, выполняющему программу, они не нужны. Другими словами, операторы segment и assume не транслируются в машинные коды, а используются лишь самим ассемблером на этапе трансляции программы. Такие нетранслируемые операторы иногда называют псевдооператорами, или директивами ассемблера в отличие от истиных операторов — команд языка.

Предложение 3, начинающееся с метки begin , является первой выполнимой строкой программы. Для того, чтобы процессор знал, с какой строки начать выполнять программу после ее загрузки в память, начальная метка программы указывается в качестве операнда самого последнего оператора программы end (см. предложение 13). Можно подумать, что указание точки входа в программу излишне: ведь как будто и так ясно, что программу надо начать выполнять с начала, а закончить, дойдя до конца. Однако в действительности для программ, написанных на языке ассемблера, это совсем не так! Текст программы может начинаться с описания подпрограмм или полей данных. В этом случае предложение программы, с которого нужно начать ее выполнение, может располагаться где-то в середине текста программы. И завершается выполнение программы совсем не обязательно в ее последних строках, а там, где стоят предложения вызова специальной программы операционной системы, предназначенной именно для завершения текущей программы и передачи управления системе (см. предложения 8. 10). Однако начиная от точки входа, программа выполняется строка за строкой точно в том порядке, в каком эти строки написаны программистом.

Предложения 5, 6 и 7 реализуют существо программы — вывод на экран строки текста. Делается это не непосредственно, а путем обращения к служебным программам операционной системы MS-DOS, которую мы для краткости будем в дальнейшем называть просто DOS. Дело в том, что в составе команд процессора и, соответственно, операторов языка ассемблера нет команд вывода данных на экран (как и команд ввода с клавиатуры, записи в файл на диске и т.д.). Вывод даже одного символа на экран в действительности представляет собой довольно сложную операцию, для выполнения которой требуется длинная последовательность команд процессора. Конечно, эту последовательность команд можно было бы включить в нашу программу, однако гораздо проще обратиться за помощью к операционной системе. В состав DOS входит большое количество программ, осуществляющих стандартные и часто требуемые функции — вывод на экран и ввод с клавиатуры, запись в файл и чтение из файла, чтение или установка текущего времени, выделение или освобождение памяти и многие другие.

Для того, чтобы обратиться к DOS, надо загрузить в регистр общего назначения AH номер требуемой функции, в другие регистры — исходные данные для выполнения этой функции, после чего выполнить команду int 21h (int — от interrupt, прерывание), которая передаст управление DOS. Вывод на экран строки текста можно осуществить функцией 09h, которая требует, чтобы в регистре DX содержался адрес выводимой строки. В предложении 6 адрес строки message загружается в регистр DX, а в предложении 7 осуществляется вызов DOS.

После последнего выполнимого предложения программы можно описывать используемые в ней данные. У нас в качестве данных выступает строка текста. Текстовые строки вводятся в программу с помощью директивы ассемблера db (от define byte, определить байт), и заключаются в апострофы. Для того, чтобы в программе можно было обращаться к данным, поля данных, как правило, предваряются именами. В нашем случае таким именем является вполне произвольное обозначение message , с которого начинается предложение 11.

Директива ends (end segment, конец сегмента) в предложении 12 указывает ассемблеру, что сегмент text закончился.

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

Все нижеизложенное в значительной степени применимо ко всем видам графических объектов (а не только текстовой (строковой) информации).

Вывод текста в окно, как способ визуализации разнообразного рода информации, является одной из основных задач в любом языке программирования. На пути изучения программирования на языке Ассемблера под Windows, очередным увесистым камнем преткновения является освоение принципов работы со строковыми данными (текстом) в рамках использования объектов графического интерфейса (окон). В последние десятилетия человечество заметно изменило механизмы взаимодействия пользователя с операционной системой в сторону использования графики, постепенно нивелируя роль текстовых режимов работы, объясняя это удобством использования. Текстовый режим в виде практически не потерявшей свой первоначальный вид консоли, ведущей своё начало еще от телетайпов и пишущих машинок, всё еще присутствует в составе перечисленных ОС. И даже в области применения современных графических интерфейсов, мы так и не отошли от основной формы выражения языка - письменности, потому как текст был и остается одним из превалирующих типов интерпретации информации. Если говорить о ранних этапах становления, то предшественницей системы Windows была MSDOS, в которой интерфейс командной строки функционировал в текстовом режиме. Помните, как в ней реализовывался вывод текста на ассемблере, который осуществлялся через функции прерывания 10h BIOS, либо попросту прямой записью данных в область видеопамяти (сегмент 0B800h ).

С одной стороны всё было довольно просто и увлекательно, ведь экран можно было представить в виде телетайпа, а вывод отождествлялся с потоком символов, автоматически "заворачивающемся" (переносящемся) при окончании очередной строки. Видеопамять отображалась на физический дисплей видеоадаптера таким образом, что можно было не заботиться о переносе строк как посредством прямого вывода в видеопамять, так и через функции прерывания 10h. Текстовый режим представлял собой матрицу ячеек фиксированного размера (8x8, 8x14, 8x16, . ), что существенно упрощало создание объектов любой сложности, начиная от одиночных символов и заканчивая полнофункциональными оконными интерфейсами. С другой стороны, во времена низкоуровневого программирования вывода под MSDOS, существовали и проблемы:

  • специфическая настройка под разные типы адаптера (CGA, VGA, SVGA): режимы, параметры вывода и прочее;
  • низкоуровневые особенности отдельных видеокарт: порты, методы задания параметров отображения, наборы команд и прочее.
  • бесконтрольный доступ к оборудованию: "в экран" мог беспрепятственно писать любой код, выполнявшийся в системе в данный момент времени.

Что же касается графики, то слишком много аспектов приходилось принимать во внимание при написании графических библиотек, учитывавших особенности распространенных видеоадаптеров. Да, мир тогда был совершенно иным и мы попросту к нему привыкли. А потом наступил Windows. Нельзя сказать, что он неожиданно постучался в двери бытия, скорее параллельно существовал, понемногу обращая на себя все более пристальное внимание сообщества, в том время как DOS пыталась удержать первенство при помощи различного рода расширителей DOS (DOS/4GW и подобные). И хотя Windows ранних версий (3.11/95/98/98se/Me) представляла собой всего-лишь графическую надстройку над DOS, она уже тогда была ориентирована на обеспечение псевдомногозадачности, в ней планировалось разделение общих аппаратных ресурсов станции между множеством "одновременно" выполняющихся приложений. К тому же, основное (в некоторых версиях излишнее) внимание в Windows было уделено использованию оконного графического интерфейса, функционал которого обеспечивался так называемым Интерфейсом графического устройства (GDI) .

GDI (Интерфейс графического устройства, Graphics Device Interface, Graphical Device Interface) — подсистема ядра, являющаяся одной из составных частей пользовательского интерфейса (оконный менеджер) Microsoft Windows. Это интерфейс (совокупность программных средств, работающих в пользовательском режиме и режиме ядра), формирующий графические объекты (точки, линии, кривые, символы, строки, фигуры, битовые массивы), шрифты и палитры, и осуществляющий их передачу на устройства отображения (монитор, принтер, память и прочее).

По описанным выше причинам, экран в Windows это уже среда, разделенная между разнообразными "одновременно" функционирующими графическими приложениями. Логично предположить, что в многозадачных операционных системах должны действовать более строгие правила, дабы исключить ситуации, когда приложения используют для вывода своей информации чужую область экрана (окно/часть окна стороннего приложения). Поэтому, вывод текста в окно графического интерфейса Windows далеко не такая тривиальная задача, как её аналог для текстового режима в MSDOS, и тут нам необходимо усвоить ряд правил:

Функции Win32 API, предназначающиеся для работы с текстом: TextOut , ExtTextOut , TabbedTextOut , DrawText , DrawTextEx , SetTextAlign , SetTextColor , PolyTextOut . Перед тем как приступить непосредственно к практической части изучения вывода текста в окно на ассемблере, необходимо сделать небольшое отступление и ознакомиться с некоторыми принципами организации графического вывода в операционных системах Windows.

Области (регионы)

Фактически объектами любого современного графического интерфейса являются окна, представляющие собой прямоугольные области произвольного размера. Пользовательское приложение, как и система, оперирует понятием окна как некой прямоугольной областью, ограничивающей вывод информации. Окно в Windows разделено на несколько областей:

  • Рабочая область (working area) это окно целиком, то есть вся область, занимаемая окном на экране;
  • Неклиентская область (non-client area) это часть окна, включающая в себя служебные элементы: рамка (бордюр), заголовок, меню, кнопки масштабирования, закрытия и восстановления и полосы прокрутки. Система по умолчанию берет на себя управление большинством аспектов неклиентской области.
  • Клиентская область (client area) это оставшаяся часть окна, предназначенная для отображения собственной информации приложения (текст/графика). Например, текстовый редактор отображает документ в клиентской области главного окна. Пользовательское приложение отвечает за обработку клиентской области окна. Для того, чтобы контролировать клиентскую область окна (принимать ввод пользователя и отображать в ней информацию), приложению необходимо инициализировать оконную процедуру.

Клиентская область это фактически регион окна приложения, за обслуживание которого отвечает само приложение. За неклиентский регион (non-client area), бордюры, заголовок, ползунок, окна, отвечает ядро системы.

window client area

Дабы приложения не "портили" окна чужих приложений, по умолчанию Windows обеспечивает контроль за выводом посредством ограничения области отрисовки каждого окна только его собственной клиентской областью.

Надо учитывать, что размер этой клиентской области окна не постоянен, поскольку пользовательская активность является причиной постоянных изменений атрибутов окна (размер, видимость и прч.). Вот именно это обстоятельство и является ключевым в понимании природы графического интерфейса. Теперь давайте поговорим о действительных и недействительных областях.

(Не)действительные области

В какой-то момент времени изображение (содержимое) окон всех приложений переносится системой в память видеоадаптера (видеопамять) для непосредственного отображения на экране. Начиная с этого момента подразумевается, что системе известно содержимое части экрана, соответствующее всем окнам, контекст которых был только что перенесен. Ядро постоянно отслеживает объекты графического интерфейса, расположенные на экране: изменения размеров окон, их перемещение. Если никаких изменений в окнах приложений не происходит, то видеоадаптер обновляет содержимое экрана, периодически выбирая его из собственной видеопамяти. Если окна приложений начинают открываться/закрываться/перемещаться, то на экране появляются области, информация об изображении которых теряет актуальность, иными словами потеряна (неизвестна) для операционной системы. В этом случае система извещает окна, области (части, фрагменты) которых стали недействительными, о необходимости восстановить (перерисовать) собственное содержимое. Ведь на рабочем столе зачастую возникают ситуации, когда пользователь перетаскивает существующее стороннее окно либо поверх содержимого нашего окна, затем закрывает/вновь перемещает его, или просто сворачивает и разворачивает окно приложения. Поскольку часть окна (или окно целиком) перекрыта содержимым находящегося выше окна, оригинальная часть замещена (скрыта) частью содержимого перекрывающего окна, поэтому часть клиентская области перекрытого окна становится недействительной. Давайте перечислим виды областей:

  • Недействительная (повреженная, требующая перерисовки) область (invalid region) - регион клиентской части окна приложения, содержимое которого потеряло актуальность и требует обновления (перерисовки).
  • Действительная (не требующая перерисовки) область (valid region) - регион клиентской области окна приложения, графическая информация о котором известна операционной системе.
  • Обновляемый регион (update region) - регион, описывающий все недействительные области определенного окна, требующие перерисовки.
  • Недействительный (повреженный, требующий перерисовки) прямоугольник (invalid rectangle) - минимальный прямоугольник в клиентской части окна приложения, включающий в себя (покрывающий) поврежденную, требующую перерисовки, область.

В зависимости от метода получения информации об области обновления окна, Windows определяет недействительную область как наименьший прямоугольник, целиком включающий в себя область, требующую обновления, либо в виде точной информации об области обновления (многоугольник), либо в полях структуры, имеющей прототип PAINTSTRUCT , возвращаемой функцией получения контекста.

Согласитесь, что было бы удобно, если бы система брала на себя абсолютно всю работу по отрисовке недействительных областей окон, однако Windows отвечает за сохранность содержимого окон далеко не всегда. Если окно перемещается по экрану при помощи клавиатуры/мыши или часть окна перекрывается собственными ниспадающими меню, то операционная система обеспечивает восстановление перекрытых областей окна. Если окно сворачивается/разворачивается, растягивается, или поверх родительского окна перемещается дочернее, то система уже не восстанавливает содержимое. И это обосновано, поскольку полная реализация подобного механизма потребовала бы от системы обработки огромного количества данных, что привело бы к непрогнозируемому падению производительности.

Если честно, перед начинающим программистом встают не такие уж и тривиальные задачи. Тут, как говорится, пока не пощупаешь все эти подходы на практике, принципа не поймешь. К примеру, если мы пишем эдакий простенький графический редактор, который предназначен для рисования мышью (абстрактная кривая) в основном окне, мы должны хранить все точки данной фигуры, потому как в случае перемещения окна нам потребуется отрисовать (восстановить) её всю целиком. Та же ситуация и с текстом, приложение должно хранить всю ту информацию, которая использовалась во всех элементах окна и на основании которой оно может в любое время окно обновить. При работе с текстовой информацией (выводе/вводе), код приложения должен сохранять данные в памяти, чтобы их можно было "по требованию" системы вывести на экран, тем самым обновив (перерисовав) актуальное содержимое окна. Таким образом, принимая во внимание всё вышеизложенное, можно сделать следующий вывод:

Приложение Windows принципиально отличается от консольной программы MSDOS тем, что хранит (и обрабатывает) одновременно начальные, промежуточные и конечные результаты своей работы. В противоположность программе MSDOS, которая обычно считывала данные и осуществляла вывод данных в процессе их последовательного поступления. Значит, программа Windows, в отличие от консольной программы, обладает "памятью прошлых периодов". И эту особенность необходимо заучить наизусть :).

Контекст устройства [отображения]

Методы получения контекста устройства

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

Читайте также: