Использование ассемблерного кода в языке Си

Отличительные особенности языка Си и описание некоторых его возможностей. Понятие программного прерывания и порядок установки собственного обработчика прерывания. Изучение возможности вставки ассемблерного кода непосредственно в исходный код на Си.

Рубрика Программирование, компьютеры и кибернетика
Вид лабораторная работа
Язык русский
Дата добавления 06.07.2009
Размер файла 66,1 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

Кафедра: Автоматика и Информационные Технологии

ИСПОЛЬЗОВАНИЕ АССЕМБЛЕРНОГО КОДА В ЯЗЫКЕ СИ

СОДЕРЖАНИЕ

  • 1. Введение
  • 2. Обращение к регистрам
  • 3. Порты ввода-вывода
  • 4. Прерывания
    • 4.1 Общие сведения
    • 4.2 Вызовы программных прерываний
    • 4.3 Установка собственного обработчика прерывания
  • 5. Ассемблерные вставки
  • 6. Работа с манипулятором «мышь»
    • 6.1 Загрузка драйвера
    • 6.2 Основные операции для работы с мышью
    • 6.3 Инициализация драйвера мыши
    • 6.4 Управление видимостью курсора
    • 6.5 Определение состояния мыши
    • 6.6 Позиционирование курсора
    • 6.7 Ограничение свободы движения курсора
    • 6.8 Ограничение на скорость перемещения курсора
    • 6.9 Форма курсора
  • 7. Практические задания
    • 7.1 Размеры текстового курсора
    • 7.2 Ожидание развертки
  • 8. Лабораторные задания
    • 8.1 Рисование мышью
    • 8.2 Элемент управления «кнопка»
  • 9. Дополнительные задания
    • 9.1 Игра
    • 9.2 Использование прерывания таймера
  • БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Введение

Традиционно считалось, что единственным языковым средством, дающим полный доступ к аппаратным средствам ЭВМ, является язык ассемблера. Применительно к персональным компьютерам IBM PC это не совсем так. Многие языки, системы программирования которых реализованы на IBM PC, в том числе язык Си, имеют в своем арсенале такие низкоуровневые средства, которые позволяют:

- напрямую обращаться к регистрам процессора;

- производить обмен данными через порты ввода-вывода;

- генерировать программные прерывания;

- обрабатывать прерывания;

- использовать ассемблерные вставки в основной текст программы.

Перечисленные возможности рассматриваются далее на примере процессора Intel 8086 (i8086). Более современные процессоры линейки x86 обратно совместимы с i8086, но имеют намного более сложную архитектуру. Кроме того, Borland C++ и операционная система MS-DOS ориентированы на процессоры до 80386, работающие в реальном режиме. В этом режиме процессор очень сильно напоминает «настоящий» i8086, поэтому особенности более совершенных моделей не рассматриваются.

2. Обращение к регистрам

Помимо ячеек оперативной памяти, для хранения данных используются запоминающие ячейки, расположенные в процессоре и называемые регистрами. Достоинство регистров заключается в их высоком быстродействии, гораздо большем, чем у оперативной памяти. Недостаток состоит в том, что регистров очень мало - всего около десятка. Поэтому регистры используются лишь для кратковременного хранения данных. В процессоре i8086 все регистры имели длину 16 разрядов. За каждым регистром закреплено определенное имя (например, АХ или DS), по которому к нему можно обращаться в программе на языке ассемблера.

В общей сложности процессор i8086 содержит двенадцать 16_разрядных программно-адресуемых регистров, которые принято объединять в три группы: регистры данных, регистры-указатели и сегментные регистры. Регистры данных и регистры-указатели часто объединяют под общим названием «регистры общего назначения» (РОН). Кроме того, в состав процессора входят указатель команд и регистр флагов (см. табл. 1).

Для РОН в табл. 1 приведено лишь их типовое назначение. Во многих арифметических, логических операциях и операциях пересылки данных РОН являются абсолютно равноправными.

Таблица 1

Регистры процессора i8086

Имя регистра

Группа

Назначение

AX, AH, AL

Регистр данных

Аккумулятор

BX, BH, BL

Регистр данных

Базовый регистр

CX, CH, CL

Регистр данных

Счетчик

DX, DH, DL

Регистр данных

Регистр данных

SI

Регистр-указатель

Индекс источника

DI

Регистр-указатель

Индекс приемника

SP

Регистр-указатель

Указатель вершины стека

BP

Регистр-указатель

Указатель базы кадра стека

CS

Сегментный регистр

Регистр сегмента кода

DS

Сегментный регистр

Регистр сегмента данных

ES

Сегментный регистр

Регистр дополнительного сегмента данных

SS

Сегментный регистр

Регистр сегмента стека

IP

-

Указатель команд

FLAGS

-

Регистр флагов

Каждый регистр данных условно делится на два отдельных 8_битных регистра: старший и младший байт, которые обозначаются так же, как и 16_битный регистр, но вторая буква для старшего - H (англ. high), для младшего - L (англ. low). Например, регистр AX состоит из старшей части AH и младшей AL (см. рис. 1). Указанные 8_битные регистры могут участвовать в различных операциях точно так же, как и 16_битные.

Рис.1. Старшая и младшая части регистра данных

В регистре флагов содержится информация о текущем состоянии арифметико-логического устройства (АЛУ) процессора. Каждый бит этого регистра соответствует некоторому состоянию и называется флагом (см. рис. 2). Например, флаг ZF показывает, равнялся ли нулю результат последней арифметической или логической операции. Часть битов в процессоре i8086 не используется, но в последующих поколениях процессоров эти биты задействуются под новые флаги. Назначение некоторых флагов приведено в табл. 2.

Рис.2. Регистр флагов

Таблица 2

Флаги процессора i8086

Номер бита

Имя

Значение

0

CF

Перенос из старшего разряда

6

ZF

Нулевой результат операции

7

SF

Знак результата (1, если отрицательный)

11

OF

Переполнение разрядной сетки

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

В языке Си существует набор псевдопеременных, позволяющих в любой точке программы получить доступ к регистрам процессора. Названия переменных в точности соответствуют названиям регистров с добавленным перед ними символом подчеркивания: _AX, _DS, _FLAGS. Значения регистров можно как прочитать из этих переменных, так и изменить путем обычного присвоения. Пользоваться данными псевдопеременными нужно с особой осторожностью: откомпилированный код по-своему распоряжается регистрами, поэтому их прямое изменение с большой долей вероятности приведет к критическим ошибкам в работе программы. В то же время чтение регистров вполне безопасно.

3. Порты ввода-вывода

Для обращения к различным устройствам ввода-вывода в архитектуре ЭВМ предусмотрен механизм обмена данными, схожий с механизмом обращения к памяти. Так же как и в случае с памятью, при чтении данных из устройства или записи данных в устройство процессор выставляет на адресную шину адрес устройства и производит обмен данными по шине данных. Устройство в этом случае ведет себя как ячейка памяти. Такая псевдоячейка называется портом ввода-вывода.

Адреса портов ввода-вывода и адреса ячеек памяти находятся в разных адресных пространствах, а для обращения к периферийным устройствам и к памяти используются разные команды. Вследствие этого один и тот же адрес может принадлежать как порту, так и ячейке памяти. Например, порт с адресом 0x60 используется для обмена с контроллером клавиатуры, а в памяти по адресу 0x60 располагается вектор прерывания 18h. Длина адреса порта составляет 16 бит, поэтому адресное пространство портов значительно меньше адресного пространства памяти и составляет всего 64 Кбайт. Тем не менее, даже из этих 64 Кбайт используется довольно небольшая часть.

Ячейки памяти могут участвовать в операциях пересылки данных, а также в большинстве арифметических и логических операций. В отличие от них порты ввода-вывода могут использоваться только для пересылки данных. Как следствие этого набор команд процессора для работы с портами очень мал и представлен фактически командой ввода in и командой вывода out.

В языке Си также имеются средства для обращения к портам ввода-вывода. Поддержка портов есть в заголовочных файлах conio.h и dos.h, причем предоставляемые этими файлами функции идентичны и отличаются только прототипами. Рассмотрим данные функции подробнее.

int inp(unsigned portid); unsigned char inportb(unsigned portid)

Функции производят чтение байта из порта с указанным адресом и возвращают прочитанный байт.

unsigned inpw(unsigned portid); unsigned inport(unsigned portid)

Функции производят чтение слова из двухбайтового порта с указанным адресом и возвращают прочитанное слово. Чтение из двухбайтового порта производится следующим образом: сначала считывается младший байт слова по адресу portid, а затем - старший по адресу (portid+1). Следует очень внимательно относиться к тому, является ли порт однобайтовым или двухбайтовым, так как двухбайтовое чтение из однобайтового порта может привести к некорректной работе программы или устройств ввода-вывода.

int outp(unsigned portid, int value); void outportb(unsigned portid, unsigned char value)

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

unsigned outpw(unsigned portid, unsigned value); void outport(unsigned portid, unsigned value)

Функции производят запись слова в двухбайтовый порт с указанным адресом. Функция outp возвращает записанное слово, то есть параметр value. запись в двухбайтовый порт осуществляется аналогично чтению: сначала записывается младший байт по адресу portid, затем - старший по адресу (portid+1). Попытка записи слова в однобайтовый порт может привести к довольно неприятным последствиям, поэтому нужно всегда уточнять разрядность порта по документации.

Для использования функций inp, inpw, outp, outpw нужно подключить заголовочный файл conio.h, для функций inportb, inport, outportb, outport - заголовочный файл dos.h.

4. Прерывания

4.1 Общие сведения

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

Запросы на прерывание могут генерироваться достаточно большим количеством устройств. Для более эффективного обслуживания прерываний запросы делятся на несколько групп, каждой из которых назначается номер прерывания. Прерыванию с определенным номером соответствует одна прерывающая программа, называемая обработчиком данного прерывания. Процессор i8086 поддерживает в общей сложности 256 прерываний, для каждого из которых может быть установлен свой обработчик. Установка обработчика производится записью его начального адреса в определенную переменную в памяти, называемую вектором прерывания. В компьютерах на базе процессора i8086 вектор прерывания содержит только дальний адрес обработчика и занимает в памяти 4 байта. Все вектора прерываний хранятся в самом начале оперативной памяти и составляют так называемую таблицу векторов прерываний. Данная таблица располагается в памяти по нулевому адресу, поэтому адрес вектора прерывания с заданным номером легко вычисляется путем умножения номера на 4.

Компьютер IBM PC поддерживает 15 различных аппаратных источников прерываний, которым соответствуют прерывания с номерами 08h - 0Fh (кроме 0Ah, которое используется внутри самого контроллера прерываний) и 70h - 77h. Назначение аппаратных прерываний приведено в табл. 3.

Таблица 3

Аппаратные прерывания IBM PC

Номер прерывания

Устройство-источник запросов

08h

Системный таймер (генерируется каждые 55 мс)

09h

Контроллер клавиатуры

0Bh

Последовательные порты COM2 и COM4

0Ch

Последовательные порты COM1 и COM3

0Dh

Параллельный порт LPT2

0Eh

Контроллер дисководов

0Fh

Параллельный порт LPT1

70h

Часы реального времени (генерируется с частотой 1024 Гц)

71h

Видеоадаптер EGA/VGA

72h

(зарезервировано)

73h

(зарезервировано)

74h

(зарезервировано)

75h

Математический сопроцессор

76h

Контроллер жестких дисков

77h

(зарезервировано)

Помимо аппаратных прерываний, существуют программные. Запросы программных прерываний генерируются самой программой. Программные прерывания обычно используются для обращения к различным системным функциям. Например, прерывание 21h позволяет вызывать большую часть низкоуровневых функций MS-DOS. Программным прерываниям назначаются все номера, не занятые аппаратными.

Вызов программных прерываний напоминает вызов некоторой функции в программе. У прерывания есть набор параметров, которые передаются при вызове через регистры процессора. Кроме того, прерывание может вернуть некоторый набор значений, также передающихся в вызывающую программу через регистры. Основным отличием программного прерывания от функции является его «глобальность»: прерывание может использоваться всеми программами, загруженными в память, в то время как функция доступна только той программе, в которой она находится (так как остальные программы не знают ни начального адреса, ни способа передачи аргументов).

Работа с прерываниями в языке Си включает два основных аспекта: вызов программного прерывания и установка собственного обработчика для конкретного прерывания.

4.2 Вызовы программных прерываний

Вызов прерываний неявно происходит при обращении ко многим встроенным функциям Си, особенно если функции осуществляют ввод или вывод информации. Прямое обращение к сервисам операционной системы через прерывания используется гораздо реже. Использование прерываний в случае, когда есть аналогичная функция библиотеки Си, нежелательно. Функции Си обеспечивают программе гораздо большую гибкость и переносимость, а также делают текст программы намного понятнее. Тем не менее, прямой вызов прерывания может потребоваться, если соответствующий сервис операционной системы или BIOS недоступен через стандартные функции, либо если функции не обеспечивают требуемой производительности.

Для вызова программных прерываний в Си существует достаточно большое количество функций, частично дублирующих друг друга. Основными функциями вызова прерываний являются int86() и int86x(). В эти функции передается номер прерывания, которое необходимо вызвать, а также две структуры типа REGS. Одна структура содержит значения регистров, которые должны быть на входе в прерывание, а во вторую функция помещает значения регистров после возврата из прерывания. Этот механизм позволяет передавать прерыванию параметры и анализировать возвращенные значения, не используя потенциально опасные обращения к псевдопеременным, которые были рассмотрены ранее.

В структуре REGS хранятся регистры общего назначения и регистр флагов. Значения сегментных регистров при вызове прерывания с помощью функции int86() остаются без изменений: регистр CS указывает на сегмент кода программы, регистр DS - на сегмент глобальных и статических переменных, значение ES не определено. Функция int86x() позволяет задавать другие значения сегментных регистров и используется, например, в тех случаях, когда прерыванию требуется передать дальний указатель через один из сегментных регистров. Значения сегментных регистров передаются в структуре типа SREGS. Текущие значения этих регистров в любой момент времени могут быть получены вызовом функции segread().

Как уже упоминалось ранее, прерывание с номером 21h используется для вызова большого количества функций DOS. Для более удобного обращения к этому прерыванию в Си существуют функции intdos() и intdosx(), аналогичные int86() и int86x(). Единственное отличие заключается в том, что intdos() и intdosx() всегда вызывают прерывание с номером 21h, поэтому в них не передается номер прерывания.

Выбор подфункции прерывания 21h, как и во многих других программных прерываниях, осуществляется занесением ее номера в регистр AH. Многие функции прерывания 21h используют для передачи параметров регистры AL, DX и DS, а результат возвращают в регистре AX. С учетом этих особенностей в Си были добавлены функции bdos() и bdosptr(), которые не требуют заполнения структуры REGS для вызова прерывания 21h. Номер подфункции, значение DX и значение AL просто указываются в качестве аргументов bdos(), а ее возвращаемое значение соответствует значению регистра AX после возврата из прерывания. Функция bdosptr() аналогична bdos(), но использует в качестве параметра указатель типа void* и может устанавливать значение регистра DS.

4.3 Установка собственного обработчика прерывания

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

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

Обработчик прерывания в Си представляет собой функцию с переменным числом аргументов, объявленную как interrupt и написанную в соответствии с требованиями к обработчикам прерываний. Основным требованием является реентерабельность обработчика, то есть возможность нормального его исполнения при прерывании его им же самим (например, когда в процессе обработки аппаратного прерывания то же самое прерывание возникает еще раз). Большинство функций DOS нереентерабельны, поэтому их запрещается использовать в обработчиках прерываний. Кроме того, обработчик должен исполняться относительно быстро и не требовать больших ресурсов памяти. Ниже приведен прототип обработчика:

void interrupt irq_timer(...);

После того как обработчик написан, его адрес следует сообщить системе, изменив соответствующий вектор прерывания. Для установки нового значения вектора прерывания используется функция setvect(), объявленная в заголовочном файле dos.h. Перед установкой обработчика следует сохранить старое значение вектора прерывания, прочитав его функцией getvect(). Это значение должно быть восстановлено перед выходом из программы, в противном случае возникновение прерывания приведет к критическим ошибкам в системе. Кроме того, правилом хорошего тона считается вызов старого обработчика в начале или в конце нового, хотя в некоторых случаях требуется как раз обратное - игнорирование старого обработчика. Здесь все определяется логикой построения конкретного обработчика и целью, которая ставилась при его написании.

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

5. Ассемблерные вставки

Помимо уже перечисленных низкоуровневых средств, Borland C++ предоставляет возможность вставки ассемблерного кода непосредственно в исходный код на Си. Это позволяет писать небольшие «ответственные» участки программы на ассемблере, не покидая оболочки Borland C++ и не разбивая файл с исходным текстом на несколько файлов, как это требуется при использовании внешнего ассемблера.

Фрагменты программы, написанные на ассемблере, вставляются в код на Си с помощью ключевого слова asm. После этого ключевого слова идет либо одна команда ассемблера, либо блок команд, заключенный в фигурные скобки:

asm mov ax,bx

asm

{

push bp

mov bp,sp

mov ax,[bp-4]

}

Код, расположенный внутри оператора asm, называется ассемблерной вставкой. Ассемблерные вставки компилируются так называемым встроенным ассемблером BASM, и полученный машинный код помещается в исполняемом файле точно в то же место функции, в котором был написан оператор asm. Ассемблер BASM несколько отличается от «внешних» ассемблеров типа TASM, MASM и др. в сторону упрощения. Тем не менее, его возможностей вполне достаточно для написания ассемблерных вставок.

Синтаксис команды ассемблера достаточно прост. Команда состоит из кода операции (в английской документации часто употребляется сокращение opcode), после которого могут присутствовать один или два операнда. Друг от друга операнды отделяются запятой, а от кода операции - пробелом:

код_операции операнд1, операнд2

Полный перечень кодов операций можно найти в документации по процессору i8086, а также в различных учебниках и справочных пособиях, в том числе электронных [7, 8].

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

Ссылки на ячейки памяти обычно выглядят как ближний адрес, записанный в квадратных скобках:

asm mov ax,[0x100]

asm mov [bx],al

Более глубокое изучение ассемблера выходит за рамки данного курса. Заинтересовавшиеся могут обратиться к учебникам и справочной литературе [7, 8].

6. РАБОТА С МАНИПУЛЯТОРОМ «МЫШЬ»

6.1 Загрузка драйвера

Широко распространенные в настоящее время манипуляторы «мышь», или просто компьютерные мыши, с точки зрения архитектуры ЭВМ представляют собой устройство ввода. Обмен информацией с мышью производится через различные порты ввода-вывода в зависимости от типа мыши и того, к какому внешнему порту компьютера она подключена. С целью упрощения прикладного программирования взаимодействие с мышью реализуется в виде отдельного модуля - драйвера, который разрабатывается производителем и поставляется с каждой мышью в соответствии с ее моделью. Прикладная программа может обращаться к драйверу мыши через программное прерывание с номером 33h.

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

После загрузки драйвера заглушка на прерывании 33h заменяется программой обработки прерывания, которая принимает информацию, поступающую от манипулятора, обрабатывает ее, информирует пользователя о состоянии мыши и управляет его работой.

В MS-DOS драйвер можно загрузить двумя способами: во время загрузки операционной системы либо после. Способ загрузки зависит от того, какое расширение имеет программа. Если драйвер имеет расширение .sys, его можно загрузить только при загрузке операционной системы. Для этого в файле config.sys необходимо записать строку:

DEVICE=MOUSE.SYS

Такой способ загрузки драйвера использовался во времена возникновения MS-DOS. Современные драйверы имеют расширение .com или .exe. Это позволяет загружать драйвер в любой момент, после загрузки операционной системы. Например, Вы можете вставить в файл autoexec.bat строку:

MOUSE.COM

Или, по своему желанию, написать в командной строке приведенную выше команду - MOUSE.COM и нажать клавишу Enter.

6.2 Основные операции для работы с мышью

Каждая программа, использующая в своей работе мышь, должна выполнить ряд операций:

- определить, работоспособен ли драйвер мыши;

- задать вид и форму курсора манипулятора;

- задать границы перемещения курсора мыши по экрану;

- задать чувствительность курсора;

- установить порог удвоенной скорости курсора мыши;

- «включить» курсор манипулятора - сделать его видимым на экране;

- задать необходимое положение курсора на экране и т. д.

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

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

6.3 Инициализация драйвера мыши

Работу с мышью следует начинать с выполнения функции 0, позволяющей проверить, загружен ли драйвер мыши в память, а если загружен, то подключена ли мышь к компьютеру. Если получены положительные ответы на оба вопроса, драйвер помещает в регистр AX значение FFFFh и проводит инициализацию. При этом указатель мыши устанавливается в центр экрана на нулевой странице и удаляется с экрана, курсор мыши принимает форму по умолчанию, и, наконец, определяется, что курсор может перемещаться по всей поверхности экрана. Кроме этого, в регистре BX процессора записывается информация о количестве кнопок мыши. Если хотя бы один ответ отрицательный, инициализация не проводится и в AX остается 0. В этом случае с мышью работать нельзя.

6.4 Управление видимостью курсора

Следующие две функции, 01h и 02h, управляют режимом видимости курсора мыши на экране. Первая выводит изображение курсора на экран, вторая удаляет его с экрана. Состояние курсора сохраняется в специальной переменной. Если значение переменной равно 0, курсор виден на экране. В противном случае - нет. Сразу после инициализации значение этой переменной равно -1. Каждое обращение к функции 02h уменьшает текущее значение на 1. Функция 01h увеличивает значение переменной на 1, но делает это только до тех пор, пока значение не станет равным нулю. Последующие вызовы функции 01h не изменяют значения переменной. Из-за этих особенностей функции следует применять попарно, чтобы не потерять курсор.

Для вызова этих функций необходимо в регистр AX записать 01h или 02h соответственно.

6.5 Определение состояния мыши

Функция 03h позволяет определить текущие координаты курсора на экране и состояние кнопок мыши. При вызове функции в регистр AX записывается номер функции. В регистрах CX и DX возвращается горизонтальная и вертикальная координата курсора. Состояние кнопок можно определить, если прочитать содержимое регистра BX.

В регистре BX используются только три первых бита. Бит принимает значение 1 (установлен) если кнопка нажата и 0 (сброшен) в противном случае. Нулевой бит отвечает за левую кнопку мыши, первый - за правую, а второй - за центральную.

Значения координат курсора, возвращаемые в регистрах CX и DX, соответствуют действительности, если вы работаете в графическом режиме. Отсчет координат ведется не с единицы, а с нуля. Поэтому левый верхний угол экрана имеет координаты (0,0), а правый нижний (количество строк-1, количество столбцов-1).

В текстовом режиме положение курсора возвращается в «виртуальных» пикселях. Чтобы перейти к нормальным координатам, строке и столбцу, надо разделить полученные значения на ширину и высоту, занимаемую одним символом на экране.

6.6 Позиционирование курсора

При перемещении манипулятора по столу драйвер перемещает курсор мыши по экрану без участия программы. Тем не менее, прикладная программа имеет возможность управлять позицией курсора. Для этого используется функция 4, которая устанавливает курсор в позицию, заданную значениями, помещенными в регистры CX (горизонтальная координата) и DX (вертикальная координата). Новые координаты задаются для графического режима в пикселях, для текстового режима - в «виртуальных» пикселях (с учетом текстовой строки и столбца). При этом значения в регистрах CX и DX должны быть в пределах диапазона, установленного для перемещения мыши.

6.7 Ограничение свободы движения курсора

Какой бы способ ни использовался для загрузки драйвера, после загрузки всегда проводится его инициализация. При этом курсор будет установлен в центр экрана и сможет перемещаться по всей его поверхности. Можно ограничить свободу курсора мыши. Чтобы сделать это, необходимо воспользоваться функциями драйвера мыши с номерами 07h и 08h. Первая функция устанавливает границы перемещения курсора по вертикали, вторая - по горизонтали. Чтобы обратиться к одной из этих функций, необходимо записать ее номер в регистр AX.

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

Минимальное значение, которое может быть записано в регистр CX, равно нулю. Максимальное значение в регистре DX не может быть больше, чем разрешение по горизонтали и вертикали.

Если Вы работаете с разрешением 800Ч600 пикселей, то значение в регистрах CX и DX может принимать значение от 0 до 599 для функции 07h, и от 0 до 799 для функции 08h.

В текстовом режиме мы, по умолчанию, имеем 25 строк по 80 столбцов (символов) в строке. Чтобы перейти от реальных текстовых строк и столбцов к «виртуальным» пикселям, необходимо от номера строки/столбца вычесть единицу и полученный результат умножить на 8.

После выполнения любой из этих функций курсор перемещается в пределы, указанные ему выше.

Для точного определения места дислокации курсора мыши предназначена функция 04h. Как всегда, в регистре AX необходимо указать номер функции - 04h. Горизонтальная и вертикальная координаты курсора задается в регистрах CX и DX, соответственно. Не забывайте, что координаты необходимо задавать в пикселях, как в функциях 07h и 08h.

6.8 Ограничение на скорость перемещения курсора

Определив размеры «резервации» и местоположение курсора в ней, Вы получаете возможность задать ограничение на скорость перемещения курсора. Прежде чем сделать это, определим, с какой скоростью он перемещается в настоящее время. Для этого существует функция 1Bh.

В регистр AX записываем 1Bh и вызываем прерывание 33h. Результаты работы программы обработки прерывания читаем из регистров BX, CX и DX. В регистре BX записана чувствительность курсора мыши по горизонтали, в CX - чувствительность курсора по вертикали и в DX - порог удвоенной скорости.

Чувствительность курсора измеряется в микки. Микки - это величина, указывающая драйверу, сколько единиц информации он должен принять от мыши, чтобы переместить курсор на один пиксель. Чувствительность может принимать значения от 1 до 32767 микки на пиксель.

Из определения микки следует, что его малые значения заставляют курсор перемещаться по экрану с реактивной скоростью. Большие значения делают курсор медлительнее черепахи.

Малое значение чувствительности мыши позволяет быстро перемещать курсор по экрану, но при этом затрудняется его точное позиционирование в нужных точках экрана. Компромисс между скоростью и точностью может быть найден, если установить такой параметр мыши, как порог удвоенной скорости. Он определяет, на сколько микки за одну секунду должна переместиться мышь, чтобы скорость перемещения курсора на экране увеличилась в 2 раза. Текущее значение этого параметра возвращается функцией 1Bh в регистре DX.

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

Оптимальные значения чувствительности мыши по вертикали и горизонтали, а также порога удвоенной скорости Вы получите, занимаясь программированием мыши. По умолчанию, после инициализации драйвера, установлены следующие значения: чувствительность мыши по горизонтали - 8 микки; по вертикали - 16 микки; порог удвоенной скорости - 64 микки/сек.

Функция 1Ah прерывания 33h предназначена для того, чтобы изменять значения любого из этих параметров. Для этого в регистр AX записываем номер функции, 1Ah, в регистр BX - новое значение чувствительности мыши по горизонтали, в CX - по вертикали, и в DX - порог удвоенной скорости перемещения. В регистрах BX и CX единицей измерения является микки. В регистре DX - микки/сек.

6.9 Форма курсора

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

Наибольший практический интерес представляет графический режим работы видеоадаптера, когда курсор имеет вид некоторого графического объекта, перемещающегося поверх изображения на экране. Этот курсор описывается некоторым блоком, имеющим прямоугольную форму. Когда такой блок перемещается по экрану и взаимодействует с пикселями изображения, находящегося «под» ним, происходит формирование фона и изображения графического курсора. Результат этого взаимодействия определяется содержимым двух массивов, размером 16Ч16 бит каждый, один из которых является маской экрана (AND-маской), другой - маской курсора (XOR-маской). Маска экрана определяет, какая часть пикселей графического блока курсора будет формировать образ курсора, а какая часть будет фоном курсора. Маска курсора определяет те пиксели, которые будут участвовать в формировании цвета курсора.

В табл. 4 представлены варианты возможных сочетаний битов AND-маски и XOR-маски и результат их взаимодействия.

Таблица 4

Бит AND-маски

Бит XOR-маски

Результирующий бит

0

0

0

0

1

1

1

0

не изменится

1

1

инвертируется

Для каждой поддерживаемой драйвером функции мыши под положением графического курсора на экране подразумевались координаты некоторой точки экрана, находящиеся под блоком курсора и носящие название «горячего пятна» (hot spot) курсора. Обычно (по умолчанию) «горячим пятном» курсора является левый верхний угол графического блока курсора с относительными координатами (0, 0), но по желанию в качестве «горячего пятна» можно выбрать любую другую точку блока.

Функция, которая устанавливает цвет, форму и задает координаты «горячего пятна» графического курсора, имеет номер 9. Как уже отмечалось выше, курсор формируется из графического блока размером 16Ч16 пикселей и определяется двумя массивами 16Ч16 бит каждый. Координаты «горячего пятна» должны находиться в диапазоне от 0 до 16. В этой связи для успешной работы функции необходимо в регистры BX и CX поместить горизонтальную и вертикальную координаты «горячего пятна» курсора, а в регистры ES:DX указатель на массивы масок (16 слов - маска экрана и 16 слов - маска курсора). Каждое слово задает значения 16 пикселей в соответствующем ряду. Младший бит соответствует крайнему правому пикселю.

Пример функции, формирующей другую форму курсора мыши (стрелка из левого нижнего угла в правый верхний), представлен ниже:

struct REGPACK ioregs;

//...

void set_cursor_shape()

{

unsigned cursor[2][16];

unsigned far *fpuns;

unsigned far **pfpuns;

ioregs.r_ax=9;

ioregs.r_bx=15;

ioregs.r_cx=0;

/* содержимое масок курсора и экрана */

cursor[1][0]=0x0001; /* 0000000000000001 */

cursor[1][1]=0x0002; /* 0000000000000010 */

cursor[1][2]=0x000e; /* 0000000000001110 */

cursor[1][3]=0x003c; /* 0000000000111100 */

cursor[1][4]=0x00fc; /* 0000000011111100 */

cursor[1][5]=0x03f8; /* 0000001111111000 */

cursor[1][6]=0x0ff8; /* 0000111111111000 */

cursor[1][7]=0x3ff0; /* 0011111111110000 */

cursor[1][8]=0xfff0; /* 1111111111110000 */

cursor[1][9]=0x3fe0; /* 0011111111100000 */

cursor[1][10]=0x1fe0; /* 0001111111100000 */

cursor[1][11]=0x3fc0; /* 0011111111000000 */

cursor[1][12]=0x78c0; /* 0111100011000000 */

cursor[1][13]=0xf000; /* 1111000000000000 */

cursor[1][14]=0xe000; /* 1110000000000000 */

cursor[1][15]=0xc000; /* 1100000000000000 */

cursor[0][0]=0xfffc; /* 1111111111111100 */

cursor[0][1]=0xfff8; /* 1111111111111000 */

cursor[0][2]=0xffe0; /* 1111111111100000 */

cursor[0][3]=0xff81; /* 1111111110000001 */

cursor[0][4]=0xfe01; /* 1111111000000001 */

cursor[0][5]=0xf803; /* 1111100000000011 */

cursor[0][6]=0xe003; /* 1110000000000011 */

cursor[0][7]=0x8007; /* 1000000000000111 */

cursor[0][8]=0x0007; /* 0000000000000111 */

cursor[0][9]=0x800f; /* 1000000000001111 */

cursor[0][10]=0xc00f; /* 1100000000001111 */

cursor[0][11]=0x801f; /* 1000000000011111 */

cursor[0][12]=0x021f; /* 0000001000011111 */

cursor[0][13]=0x07ff; /* 0000011111111111 */

cursor[0][14]=0x0fff; /* 0000111111111111 */

cursor[0][15]=0x1fff; /* 0001111111111111 */

fpuns=(unsigned far *)cursor;

pfpuns=&fpuns;

ioregs.r_es=*((unsigned *)pfpuns+1);

ioregs.r_dx=*(unsigned *)pfpuns;

intr(0x33,&ioregs);

}

7.Практические задания

7.1 Размеры текстового курсора

Написать функцию, позволяющую устанавливать произвольный размер текстового курсора.

Известно, что в текстовом режиме видеоадаптера на экране присутствует курсор, внешний вид которого можно поменять функцией _setcursortype(). Данная функция позволяет установить один из трех стандартных типов курсора: «подчеркивание», «блок» или отключенный. В то же время BIOS предоставляет более широкие возможности по управлению формой курсора. Форма курсора определяется его начальной и конечной координатами по вертикали внутри знакоместа, а сам курсор представляет собой прямоугольник шириной во все знакоместо с заданными координатами верхней (начальной) и нижней (конечной) границ.

Для изменения внешнего вида курсора используется функция 01h прерывания 10h. При вызове данного прерывания необходимо установить регистр AH в значение 01h (номер функции), в регистре CH передать начальную координату курсора, а в регистре CL - конечную.

Требуется написать функцию, принимающую в качестве параметров новую начальную координату и высоту курсора.

7.2 Ожидание развертки

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

Достаточно часто при написании программ, интенсивно работающих с текстовым содержимым экрана, возникает проблема «мерцания» текста в обновляемой области. Это связано с модификацией области программой в тот момент, когда видеоадаптер осуществляет вывод этой области на экран монитора (иначе говоря, электронный луч пробегает данную область). Для устранения мерцаний используются различные подходы. Один из них - обновление экрана во время вертикального обратного хода луча развертки.

Ожидание начала обратного хода производится путем опроса состояния видеоадаптера. Байт состояния можно прочитать из порта с адресом 0x3DA. Бит 0 в считанном значении отвечает за обратный ход в общем случае (установлен в 1 как при вертикальном, так и при горизонтальном обратном ходе), а бит 3 позволяет отличить вертикальный ход от горизонтального (установлен в 1 при вертикальном ходе).

Таким образом, функция, ожидающая начала вертикального обратного хода, должна опрашивать в цикле порт 0x3DA и дождаться перехода его из состояния, в котором хотя бы один из битов 0 и 3 равен 0, в состояние xxxx1xx1 (вертикальный обратный ход). После перехода в нужное состояние производится выход из функции.

Замечание: следует дожидаться именно указанного перехода, простое ожидание состояния xxxx1xx1 может привести к ложному срабатыванию в середине вертикального обратного хода.

8.Лабораторные задания

8.1 Рисование мышью

Написать простейший графический редактор. Программа должна предоставлять пользователю возможность рисования на экране в графическом режиме. Рисование производится путем перемещения мыши с нажатой левой клавишей.

8.2 Элемент управления «кнопка»

Реализовать программно элемент управления «кнопка». Кнопка должна иметь два состояния: нажатое и не нажатое, в зависимости от состояния должен меняться ее внешний вид (объемное изображение утопленной или приподнятой кнопки). Нажатие и удержание кнопки производится левой клавишей мыши. При наведении на кнопку курсор мыши должен менять форму.

9.Дополнительные задания

9.1 Игра

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

Замечания:

На время отрисовки или стирания объекта необходимо отключать курсор мыши.

Стирание объекта можно производить различными способами: выводом того же объекта с режимом setwritemode(XOR_PUT) или очисткой экрана. Вместо очистки всего экрана функцией cleardevice() рекомендуется очищать окно минимальных размеров, используя функции setviewport() и clearviewport().

9.2 Использование прерывания таймера

Одним из аппаратных прерываний является прерывание таймера, имеющее номер 08h. Данное прерывание вызывается каждые 55 миллисекунд (что соответствует частоте приблизительно 18,2 Гц) и часто используется для выполнения небольших фоновых задач.

Требуется написать обработчик прерывания 08h, который выводил бы в правом верхнем углу экрана текущее время. В качестве тела программы использовать диалог на основе ввода строк с редактированием. При этом программа задает пользователю ряд вопросов и выводит некоторые сведения на основе полученных ответов. Простейший пример - запрос имени и вывод фразы «Привет, <введенное_имя>!».

Замечания:

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

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

Для вывода текста на экран внутри обработчика прерывания нельзя использовать функции DOS и BIOS, в частности, printf(). Это объясняется их нереентерабельностью. Разрешается использовать функции, осуществляющие прямой вывод в видеопамять, например, cprintf() при включенном режиме directvideo=1.

По окончании работы программы необходимо восстановить старое значение вектора прерывания 08h.

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Керниган Б. Язык программирования Си / Б. Керниган, Д. Ритчи. М.: Финансы и статистика, 1992. 272 с.

2. Керниган Б. Язык программирования Си. Задачи по курсу Си / Б. Керниган, Д. Ритчи. М.: Финансы и статистика, 1985. 192 с.

3. Юркин А.Г. Задачник по программированию / А.Г. Юркин. СПб.: Питер, 2002. 192 с.

4. Подбельский В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. М.: Финансы и статистика, 2005. 600 с.

5. Трофимов С.П. Программирование в Си. Организация ввода-вывода: метод. указания / С.П. Трофимов. Екатеринбург: УГТУ,1998. 20 с.

6. Трофимов С.П. Программирование в Си. Динамически распределяемая память: метод. указания / С.П. Трофимов. Екатеринбург: МИДО, 1998. 14 с.

7. Юров В. Assembler. Учебник / В. Юров. СПб.: Питер, 2001. - 624 с., ил.

8. Электронный справочник Flambeaux Software's TECH Help версии 6.0. / Dan Rollins, 1994.

9. Хмелевский И.В. Организация ЭВМ и систем. Однопроцессорные ЭВМ. Часть 2.: Конспект лекций / И.В. Хмелевский, В.П. Битюцкий. 2-е изд., испр. и допол. Екатеринбург: ГОУ ВПО УГТУ-УПИ, 2005. - 98 с.

10. Фролов А. Операционная система MS-DOS. Том 1 / А. Фролов, Г. Фролов. М.: Диалог-МИФИ, 1991. - 239 с.

11. Фролов А. Аппаратное обеспечение IBM PC. Том 2 / А. Фролов, Г. Фролов. М.: Диалог-МИФИ, 1992.


Подобные документы

  • Изучение объектно-ориентированного языка программирования Java, его функциональные возможности. Создание программного кода. Описание классов и методов, использованных в программе. Руководство пользователя, запуск сервера и клиентского приложения.

    курсовая работа [1,8 M], добавлен 16.09.2015

  • Изучение устройства и механизма процессов в компиляторах и интерпретаторах. Понятие трансляции как процедуры перевода программного кода с языка Паскаль на язык С++. Описание интерфейса программы и автоматизация процесса построения диаграммы классов.

    курсовая работа [536,2 K], добавлен 03.07.2011

  • Создание программы для вычисления значения функции на основе определённой формулы. Уточнение структуры входных и выходных данных и определение ассемблерного формата их представления. Разработка алгоритмов для реализации работы программного обеспечения.

    курсовая работа [240,6 K], добавлен 17.06.2013

  • Структура и назначение программного кода программы по созданию 3D-графики средствами языка программирования Visual Basic. Элементы управления "Окно формы" и "Таймер", выполняемые ими функции и основные свойства, значение в работе программного кода.

    лабораторная работа [362,4 K], добавлен 06.07.2009

  • Обзор существующих технологий разработки программного обеспечения. Описание платформы NET Framework. Принцип работы платформы: компиляция исходного кода; процесс загрузки и исполнения кода; IL-код и верификация. Новые возможности платформы NET Framework.

    реферат [30,7 K], добавлен 01.03.2011

  • Многообразие мини-игр и возможности языка Visual basic 6.0 для их реализации. Понятие мини-игр и их классификация. Элементы управления мини-игры "Реверси". Разработка прикладной программы. Создание игрового интерфейса. Написание программного кода.

    курсовая работа [1,5 M], добавлен 03.06.2014

  • Описание и особенности некоторых алгоритмов архивации. Построение кода Хаффмана. Динамический алгоритм построения кода Хаффмана. Обратное восстановление текста. Способы двухступенчатого кодирования информации. Практическая реализация алгоритма LZ77.

    курсовая работа [51,7 K], добавлен 24.12.2012

  • Исследование методов оптимизации программного кода на языке Си с помощью компилятора. Тестирование результатов утилитой optbench.c. Определение особенностей оптимизации компилятора на собственной программе. Удачные примеры быстроты и компактности кода.

    лабораторная работа [26,5 K], добавлен 17.12.2012

  • Разработка программного продукта с помощью языка программирования Visual Basic. Описание интерфейса пользователя и возможностей программы. Исходный код основных модулей. Программа, демонстрирующая основные возможности диаграмм и среды Visual Basic.

    контрольная работа [989,9 K], добавлен 29.03.2011

  • Подключение периферийных устройств к ЭВМ. Синхронизация выполнения программы с внешними процессами. Прерывания. Реализация механизма прерывания в х86. Прямой доступ к памяти. Шины, магистраль PCI. Процесс загрузки компьютера. Клавиатура, системный таймер.

    презентация [7,1 M], добавлен 14.12.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.