
- •354000 «Прикладная информатика»
- •Idiv Деление целых знаковых чисел
- •Imul Умножение целых знаковых чисел
- •In Ввод из порта
- •Inc Инкремент (увеличение на 1)
- •Int Программное прерывание
- •I into Прерывание по переполнению
- •Iret Возврат из программы обработки прерывания
- •Xchg Обмен данными между операндами
- •Xlat Табличная трансляция
- •Xor Логическое исключающее или
- •Загрузка и выполнение программ
- •Составление программы на языке Assembler и её размещение в памяти компьютера. Стандартное распределение памяти под программные сегменты.
- •Оператор Текст
- •Имя Операнд_1 ;
- •Имя Операнд_1
- •Загрузка и выполнение программ
- •Mov dl,10 ;Позиция X
- •In Считать данные из порта.
- •Исследование команд, выполняемых процессором. Программирование операций с помощью отладчика языка Assembler..
- •Исследование операций с памятью компьютера. Программирование на языка Assembler в операционной системе ms-dos. Обычная память
- •Косвенная адресация
- •Адресация по базе со сдвигом
- •Косвенная адресация с масштабированием
- •Адресация по базе с индексированием
- •Программирование прерываний на языке Assembler в операционной системе ms-dos.
- •Исследование механизмов работа с видеопамятью персонального компьютера типа ibm. Тестирование и отладка ассемблерных программ.
Xchg Обмен данными между операндами
Команда XCHG пересылает значение первого операнда во второй, а второго - в первый. В качестве операндов можно указывать регистр (кроме сегментного) или ячейку памяти, а в качестве второго операнда еще и непосредственное значение, однако не допускается определять оба операнда одновременно как ячейки памяти. Операнды могут быть байтами или словами со знаком или без знака.
Xlat Табличная трансляция
Команда XLAT осуществляет выборку байта из таблицы. В регистре ВХ должен находиться относительный адрес таблицы, а в регистре AL - смещение в таблице к выбираемому байту (его индекс). Выбранный байт загружается в регистр AL, замещая находившееся в нем смещение. Длина таблицы может достигать 256 байтов. Команда XLAT не имеет явных операндов, но требует предварительной настройки регистров ВХ и AL.
Xor Логическое исключающее или
Команда XOR выполняет операцию логического (побитового) ИСКЛЮЧАЮЩЕГО ИЛИ над двумя операндами. Результат операции замещает первый операнд. Каждый бит результата устанавливается в 1, если соответствующие биты операндов различны, и сбрасывается в 0, если соответствующие биты операндов совпадают.
В качестве первого операнда можно указывать регистр (кроме сегментного) или ячейку памяти, в качестве второго - регистр (кроме сегментного), ячейку памяти или непосредственное значение, однако не допускается определять оба операнда одновременно как ячейки памяти. Операнды могут быть байтами или словами.
2 ОСОБЕННОСТИ НАПИСАНИЯ ДРАЙВЕРОВ
Драйверы устройств в DOS
Прикладные программы обычно никогда не используют уровень портов входа-выхода, а обращаются ко всем устройствам через средства операционной системы. DOS, в свою очередь, обращается к средствам BIOS, которые осуществляют взаимодействие на уровне портов со всеми стандартными устройствами. Фактически процедуры BIOS и выполняют функции драйверов устройств - программ, осуществляющих интерфейс между операционной системой и аппаратной частью компьютера. BIOS обычно лучше всего известно, как управлять устройствами, которые поставляются вместе с компьютером, но, если требуется подключить новое устройство, о котором BIOS ничего не знает, появляется необходимость в специально написанном загружаемом драйвере.
Драйверы устройств в DOS - исполняемые файлы со специальной структурой, которые загружаются на этапе запуска (при выполнении команд DEVICE или DEVICEHIGH файла config.sys) и становятся фактически частью системы. Драйвер всегда начинается с 18-байтного заголовка:
+00: 4 байта -дальний адрес следующего загружаемого драйвера DOS - так как в момент загрузки драйвер будет последним в цепочке, адрес должен быть равен 0FFFFh:0FFFFh
+04: 2 байта - атрибуты драйвера
+06: 2 байта - адрес процедуры стратегии
+08: 2 байта - адрес процедуры прерывания
+OAh: 8 байт - имя драйвера для символьных устройств (дополненное пробелами) для блочных устройств - байт по смещению OAli включает число устройств, поддерживаемых этим драйвером, а остальные байты могут содержать имя драйвера
Здесь следует заметить, что DOS поддерживает два типа драйверов - символьного и блочного устройств. Первый тип используется для любых устройств -клавиатуры, принтера, сети, а второй - только для устройств, на которых могут существовать файловые системы, то есть для дисководов, RAM-дисков, нестандартных жестких дисков, для доступа к разделам диска, занятым другими операционными системами, и т. д. Для работы с символьным устройством программа должна открыть его при помощи функции DOS «открыть файл или устройство», а для работы с блочным устройством - обратиться к соответствующему логическому диску.
Итак, код драйвера устройства представляет собой обычный код программы, как и в случае с СОМ-файлом, но в начале не надо размещать директиву org 100h для пропуска PSP. Можно также объединить драйвер и исполняемую программу, разместив в ЕХЕ-файле код драйвера с нулевым смещением от начала сегмента, а точку входа самой программы ниже.
При обращении к драйверу DOS сначала вызывает процедуру стратегии (адрес по смещению 06 в заголовке), передавая ей адрес буфера запроса, содержащий все параметры, передаваемые драйверу, а затем процедуру прерывания (адрес по смещению 08) без каких-либо параметров. Процедура стратегии должна сохранить адрес буфера запроса, а процедура прерывания - собственно выполнить все необходимые действия. Структура буфера запроса меняется в зависимости от типа команды, передаваемой драйверу, но структура его заголовка остается постоянной:
+00h: байт - длина буфера запроса (включая заголовок)
+01h: байт - номер устройства (для блочных устройств)
+02h: байт - код команды (00h - 19h)
+03h: 2 байта - слово состояния драйвера - должно быть заполнено драйвером
бит 15: произошла ошибка
биты 10-14: 00000b
бит 9: устройство занято
бит 8: команда обслужена
биты 7-0: код ошибки
00h: устройство защищено от записи
0lh: неизвестное устройство
02h: устройство не готово
03h: неизвестная команда
04h: ошибка CRC
05h: ошибка в буфере запроса
06h: ошибка поиска
07h: неизвестный носитель
08h: сектор не найден
09h: нет бумаги
0Ah: общая ошибка записи
0Вh: общая ошибка чтения
0Сh: общая ошибка
0Fh: неожиданная смена диска
+05h: 8 байт - зарезервировано
+0Dh: отсюда начинается область данных, отличающаяся для разных команд
Даже если драйвер не поддерживает запрошенную от него функцию, он обязательно должен установить бит 8 слова состояния в 1.
Символьные устройства
Драйвер символьного устройства должен содержать в поле атрибутов драйвера (смещение 04 в заголовке) единицу в самом старшем бите. Тогда остальные биты трактуются следующим образом:
бит 15: 1
бит 14: драйвер поддерживает функции чтения/записи IOCTL
бит 13: драйвер поддерживает функцию вывода до занятости
бит 12: 0
бит 11: драйвер поддерживает функции открыть/закрыть устройство
биты 10-8:000
бит 7: драйвер поддерживает функцию запроса поддержки IOCTL
бит 6: драйвер поддерживает обобщенный IOCTL
бит 5: 0
бит 4: драйвер поддерживает быстрый вывод (через INT 29h)
бит 3: драйвер устройства «часы»
бит 2: драйвер устройства NUL
бит 1: драйвер устройства STDOUT
бит 0: драйвер устройства STDIN
IOCTL - это большой набор функций (свыше пятидесяти), доступных как различные подфункции INT 21h АН = 44h и предназначенных для прямого взаимодействия с драйверами.
Рассмотрим драйвер, который вообще не обслуживает никакое устройство, реальное или виртуальное, а просто увеличивает размер буфера клавиатуры BIOS до 256 (или больше) символов. Этого можно было бы добиться обычной резидентной программой, но BIOS хранит в своей области данных только ближние адреса для этого 6yфepa, то есть смещения относительно сегментного адреса 0040h. Так как драйверы загружаются в память первыми, еще до командного интерпретатора, они обычно попадают в область линейных адресов 00400h -10400h, в то время как с резидентными программами этого может не получиться.
Драйвер, обрабатывающий только одну команду, команду инициализации драйвера 00h. Для нее буфер запроса выглядит следующим образом:
+00h: байт - 19h (длина буфера запроса)
+01h: байт - не используется
+02h: байт - 00 (код команды)
+03h: байт - слово состояния драйвера (заполняется драйвером)
+05h: 8 байт - не используется
+0Dh: байт - число обслуживаемых устройств (заполняется блочным драйвером)
+0Eh: 4 байта - на входе - конец доступной для драйвера памяти; на выходе -адрес первого байта из той части драйвера, которая не будет резидентной (чтобы выйти без инсталляции - здесь надо записать адрес первого байта)
+12Ь: 4 байта - на входе - адрес строки в CONFIG.SYS, загрузившей драйвер; на выходе - адрес массива ВРВ (для блочных драйверов)
+16h: байт - номер первого диска
+17h: 2 байта -сообщение об ошибке (0000h, если ошибки не было) - заполняется драйвером
Процедура инициализации может использовать функции DOS Olh – 0Ch, 25h, 30h и 35h.
Рассмотрим функции, которые должен поддерживать ROT13 — драйвер символьного устройства. ROT13 - это метод простой модификации английского текста, применяющийся в электронной почте, чтобы текст нельзя было прочитать сразу. Методика заключается в сдвиге каждой буквы латинского алфавита на 13 позиций (в любую сторону, так как всего 26 букв). Раскодирование, очевидно, выполняется такой же операцией. Когда драйвер загружен, команда DOS
copy encrypt.txt rot13
приведет к тому, что текст из encrypt.txt будет выведен на экран, зашифрованный или расшифрованный ROT13, в зависимости от того, был ли он зашифрован до этого.
Рассмотрим все команды, которые может поддерживать символьное устройство, и буфера запросов, которые им передаются.
00h: инициализация (уже рассмотрена)
03h: IOCTL-чтенне (если установлен бит 14 атрибута)
+0Eh: 4 байта - адрес буфера
+12h:2 байта - на входе - запрашиваемое число байтов
на выходе - реально записанное в буфер число байтов
004h: чтение из устройства структура буфера для символьных устройств совпадает с 03h
05h: чтение без удаления символа из буфера
+ODh: на выходе - прочитанный символ, если символа нет - установить бит 9 слова состояния 06h: определить состояние бу4)ера чтения, если в буфере нет символов для чтения - установить бит 9 слова состояния
07h: сбросить буфер ввода
08h: запись в устройство
+OEh: 4 байта - адрес буфера
+12h: 2 байта - на входе - число байтов для записи
на выходе - число байтов, которые были записаны
09h: запись в устройство с проверкой аналогично 08h
0Ah: определить состояние буфера записи, если в устройство нельзя писать - установить бит 9 слова состояния
0Bh: сбросить буфер записи
0Ch: IOCTL-запись (если установлен бит 14 атрибута), аналогично 08h
0Dh: открыть устройство (если установлен бит 11 атрибута)
0Eh: закрыть устройство (если установлен бит 11 атрибута)
11h: вывод, пока не занято (если установлен бит 13 атрибута), аналогично 08h в отличие от функций записи здесь не считается ошибкой записать не вce байты
13h: обобщенный IOCTL (если установлен бит 6 атрибута)
+ODh: байт - категория устройства (01, 03, 05 = СОМ, CON, LPT)
00h - неизвестная категория
+0Eh: байт - код подфункции:
45h: установить число повторных попыток
65h: определить число повторных попыток
4Ah: выбрать кодовую страницу
6Ah: определить активную кодовую страницу
4Ch: начало подготовки кодовой страницы
4Dh: конец подготовки кодовой страницы
6Bh: получить список готовых кодовых страниц
5Fh: установить информацию о дисплее
7Fh: получить информацию о дисплее
+0Fh: 4 байта - не используются
+13h: 4 байта - адрес структуры данных IOCTL - соответствует структуре, передающейся в DS:DX для INT 21h, AX = 440Ch
19h: поддержка функций IOCTL (если установлены биты 6 и 7 атри-бута)
+0Dh: байт - категория устройства
+0Eh: байт - код подфункции
Если эта комбинация подфункции и категории устройства не поддерживается драйвером - надо вернуть ошибку ОЗП в слове состояния.
Создадим драйвер символьного устройства. Упрощая задачу, реализуем только функции чтения из устройства и будем возвращать соответствующие ошибки для других функций.
Блочные устройства
Блочными устройствами называются такие устройства, на которых DOS может организовать файловую систему. DOS не работает напрямую с дисками через BIOS, а только с драйверами блочных устройств, каждое из которых представляется системе как линейный массив секторов определенной длины (обычно 512 байт) с произвольным доступом (для BIOS, к примеру, диск - это четырехмерный массив секторов, дорожек, цилиндров и головок). Каждому загруженному устройству DOS присваивает один или несколько номеров логических дисков, которые соответствуют буквам, используемым для обращения к ним. Так, стандартный драйвер дисков получает буквы А, В, С и так далее, по числу видимых разделов на диске.
Рассмотрим атрибуты и команды, передающиеся блочным устройствам.
Атрибуты:
бит 15: 0 (признак блочного устройства)
бит 14: поддерживаются IOCTL-чтение и запись
бит 13: не требует копию первого сектора FAT, чтобы построить ВРВ
бит 12: сетевой диск
бит 11: поддерживает команды открыть/закрыть устройство и проверить, является ли устройство сменным
биты 10-8:000
бит 7: поддерживается проверка поддержки IOCTL
бит 6: поддерживается обобщенный IOCTL и команды установить и определить номер логического диска биты 5-2:0000
бит 1: поддерживаются 32-битные номера секторов бит 0: 0
Команды и структура переменной части буфера запроса для них (только то, что отличается от аналогичных структур для символьных устройств):
00h: инициализация
+0Dh: - байт количество устройств, которые поддерживает драйвер
+12h: 4 - байта дальний адрес массива ВРВ-структур (по одной для каждого устройства)
ВРВ - это 25-байтная структура (53 для FAT32), которая описывает блочное устройство. Ее можно найти по смещению 0Bh от начала нулевого сектора па любом диске:
+0: 2 байта - число байтов в секторе (обычно 512)
+2: байт - число секторов в кластере (DOS выделяет пространство па диске для файлов не секторами, а обычно более крупными единицами - кластерами. Даже самый маленький файл занимает один кластер)
+3: 2 байта - число секторов до начала FAT (обычно один - загрузочный)
+5: байт - число копий FAT (обычно 2) (FAT - это список кластеров, в которых расположен каждый файл. DOS делает вторую копию, чтобы можно было восстановить диск, если произошел сбой во время модификации FAT)
+6: 2 байта - максимальное число файлов в корневой директории +8- 2 байта - число секторов на устройстве (если их больше 65 536 - здесь записан 0)
+0Ah: байт - описатель носителя (0F8h - для жестких дисков, 0F0h - для дискет на 1,2 и 1,44 Мб, а также других устройств)
+0ВП:2 байта - число секторов в одной копии FAT (0, если больше 65 535)
+0Dh:2 байта - число секторов па дорожке (для доступа средствами BIOS)
+0Fh: 2 байта - число головок (для доступа средствами BIOS)
+11h: 4 байта - число скрытых секторов
+15h: 4 байта - 32-битное число секторов на диске
(следующие поля действительны только для дисков, использующих FAT32)
+16h: 4 байта - 32-битное число секторов в FAT
+1Dh:байт -флаги
бит 7: не обновлять резервные копии FAT
биты 3-0: номер активной FAT, если бит 7=1
+lFh: 2 байта - версия файловой системы (0000h для Windows 95 OSR2)
+21h: 4 байта - номер кластера корневой директории
+25h: 2 байта - номер сектора с информацией о файловой системе (0FFFFh, если он отсутствует)
+27h: 2 байта - номер сектора запасной копии загрузочного сектора (0FFFFh, если отсутствует)
+29h: 12 байт - зарезервировано
Для всех остальных команд в поле буфера запроса со смещением +1 размещается номер логического устройства из числа обслуживаемых драйвером, к которому относится команда:
01h: проверка носителя
+ODh: бант - на входе - описатель носителя
на выходе - 0FFh, если диск был сменен
0lh, если диск не был сменен
00h, если это нельзя определить
+OFh: 4 байта - адрес ASCIZ-строки с меткой диска (если установлен бит 11 в атрибуте)
02h: построить ВРВ
+0Dh: описатель носителя
+0Eh: 4 байта - на входе - дальний адрес копни первого сектора FAT на выходе - дальний адрес ВРВ
03h: IOCTL-чтение (если установлен бит 14 атрибута)
04h:чтение из устройства
+ODh: байт - описатель носителя
+12h: 2 байта - на входе - число секторов, которые надо прочитать
на выходе - число прочитанных секторов
+16h: 2 байта - первый сектор (если больше 65 535 - здесь 0FFFFh)
+18h: 4 байта - на выходе - адрес метки диска, если произошла
ошибка 0Fh
+lCh: 4 байта - первый сектор
08h: запись в устройство
структура буфера аналогична 04h с точностью до замены чтения на
запись
09h: запись в устройство с проверкой аналогично 08h
0Сh: IOCTL-запись (если установлен бит 14 атрибута)
0Dh: открыть устройство (если установлен бит 11 атрибута)
0Eh: закрыть устройство (если установлен бит 11 атрибута)
0Fh: проверка наличия сменного диска (если установлен бит 11
атрибута):
драйвер должен установить бит 9 слова состояния, если диск сменный,
и сбросить, если нет
13h: обобщенный IOCTL (если установлен бит 6 атрибута)
+ODh: байт - категория устройства:
08h: дисковое устройство
48h: дисковое устройство с FAT32
+OEh: код - подфункции:
40h: установить параметры
60h: прочитать параметры
41h: записать дорожку
42h: отформатировать и проверить дорожку
62h: проверить дорожку
46h: установить номер тома
66h: считать номер тома
47h: установить флаг доступа
67h: прочитать флаг доступа
68h: определить тип носителя (DOS 5.0+)
Драйверы устройств
В Windows, так же как и в DOS, существует еще один вид исполняемых файлов - драйверы устройств. Windows 3.хх и Windows 95 используют одну модель драйверов, Windows NT - другую, a Windows 98 - уже третью, хотя и во многом близкую к модели Windows NT. В Windows 3.хх/95 применяются два типа драйверов устройств - виртуальные драйверы (VxD), выполняющиеся с уровнем привилегий 0 (обычно имеют расширение .386 для Windows 3.хх и .VXD для Windows 95), и непривилегированные драйверы, исполняющиеся, как и обычные программы, с уровнем привилегий 3 (как правило, они имеют расширение .DRV). Windows NT использует несовместимую модель драйверов, так называемую kernel-mode (режим ядра). На основе модели kernel-mode с добавлением поддержки технологии PNP и понятия потоков данных в 1996 году была создана модель WDM (win32 driver model), которая теперь используется в Windows 98/NT и, по-видимому, будет играть главную роль в дальнейшем.
Как и следовало ожидать, основным средством создания драйверов является ассемблер, и хотя использование С здесь возможно (в отличие от драйверов DOS), но сделать это сложнее: функции, к которым обращается драйвер, могут передавать параметры в регистрах; сегменты, из которых состоит драйвер, должны называться определенным образом и т. д. При создании драйверов часто пользуются одновременно С и ассемблером или С и специальным пакетом, который включает в себя все необходимые для инициализации средства.
Чтобы самостоятельно создавать драйверы для любой версии Windows, необходим комплект программ, документации, включаемых файлов и библиотек, распространяемый Microsoft, который называется DDK - Drivers Development Kit. (DDK для Windows NT/98 распространяются бесплатно.)
Чтобы создать драйвер, лучше всего начать с одного из прилагающихся примеров и изменять/добавлять процедуры инициализации, обработчики сообщений, прерываний и исключении, обработчики для API, предоставляемого драйвером, и т. д. Рассмотрим, как выглядит исходный текст драйвера, потому что он несколько отличается от привычных ассемблерных программ.
Любой драйвер начинается с директивы include vmm.inc, которая включает файл, содержащий определения используемых сегментов и макроопределений.
Макроопределения вида VXD_LOCKED_CODE_SEG/VXD_ LOCKED_CODE_ ENDS соответствуют директивам начала и конца сегментов (в данном случае сегмента _LTEXT). Другие важные макроопределения - Declare_Virtual_Device и VMMCall/WDMCall. Первое - просто определение, получающее в качестве параметров идентификатор драйвера, название, версию, порядок загрузки и адреса основных процедур драйвера, из которых строится его заголовок. Второе - замена команды call, получающая в качестве параметра имя функции VMM или WDM, к которой надо обратиться.