Dos fn 35h дать вектор прерывания


Содержание

Работа с векторами прерываний.

Система прерываний.

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

Адреса подпрограмм обслуживания прерываний находятся в специальной таблице и называются векторами прерывания. В реальном режиме таблица вектров распологается в начале физической памяти; вектор имеет длину четыре байта и храниться в форме CS:IP. В защищенном режиме таблица векторов может быть расположена в любом месте и содержит более сложные дескрипторы (в режиме V86 имеется подобие таблицы реального режима).

Работа с системой прерываний может рассматриваться с двух точек зрения:

1. Работа с векторами прерываний;

2. Работа с микросхемами контроллеров прерываний (современные рашины могут не иметь отдельной микросхемы контроллера прерываний, но их регистры сохранены в адресном пространстве).

ПРИМЕЧАНИЕ: Еще раз подчеркивается, что речь идет прежде всего о реальном режиме. Попытка «попробовать» в любом другом может иметь непредсказуемые последствия.

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

При работе в режиме V86 возможно изменение образа таблицы прерываний, но последствия определяются возможностями виртуального монитора!

Работа с векторами прерываний.

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

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

2. Второй причиной написания прерывания может быть использование какого-либо отдельного аппаратного прерывания. Это прерывание автоматически вызывается при возникновении определенных условий.
Обычно BIOS инициализирует неиспользуемые ею вектора прерываний так, что они указывают на процедуру, которая вообще ничего не делает (она содержит один оператор IRET). Вы можете написать свою процедуру и изменить вектор прерываний, чтобы он указывал на нее. Тогда при возникновении аппаратного прерывания будет выполняться Ваша процедура.

3. Возможна подмена существующего прерывания на ваше собственное. Одно из таких прерываний это прерывание времени суток, которое автоматически вызывается 18.2 раза в секунду. Обычно это прерывание только обновляет показание часов, но Вы можете использовать его для синхронизации событий внутри Вашей программы. Другие возможности — это написание процедуры обработки Ctrl-Break если Ваша программа должна выполнять некие специфические действия перед своим завершением.

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

Таблица векторов прерываний занимает 1Кбайт памяти в диапазоне 00000h — 00400h. Каждому вектору отводится четыре байта в таблице и присваивается номер от 00h до FFh. При инициализации системы вектора настраиваются на подпрограммы, расположенные в ROM BIOS или DOS.

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

Функция 25H прерывания 21H устанавливает вектор прерывания на указанный адрес. Адреса имеют размер два слова. Старшее слово содержит значение сегмента (CS), младшее содержит смещение (IP). Чтобы установить вектор, указывающим на одну из Ваших процедур, нужно поместить сегмент процедуры в DS, а смещение в DX Затем поместите номер прерывания в AL и вызовите функцию. Функция 25H автоматически запрещает аппаратные прерывания в процессе изменения вектора, поэтому не существует опасности, что посреди дороги произойдет аппаратное прерывание, использующее данный вектор.

PUSH DS ;сохраняем DS

MOV DX,OFFSET ROUT ;смещение для процедуры в DX

MOV AX,SEG ROUT ;сегмент процедуры

MOV DS,AX ;помещаем в DS

MOV AH,25H ;функция установки вектора

MOV AL,60H ;номер вектора

INT 21H ;меняем прерывание

POP DS ;восстанавливаем DS

Функция 35 прерывания 21H возвращает текущее значение вектора прерывания, помещая значение сегмента в ES, а смещение в BX. Перед установкой своего прерывания получите текущее значение вектора, используя эту функцию, сохраните эти значения, и затем восстановите их с помощью функции 25H (как выше) перед завершением своей программы.
Например:

;—в сегменте данных:

KEEP_CS DW 0 ;хранит сегмент заменяемого прерывания

KEEP_IP DW 0 ;хранит смещение прерывания

;—в начале программы

MOV AH,35H ;функция получения вектора

MOV AL,1CH ;номер вектора

INT 21H ;теперь сегмент в ES, смещение в BX

MOV KEEP_IP,BX ;запоминаем смещение

MOV KEEP_CS,ES ;запоминаем сегмент

;—далее можно изменять вектор по своему усмотрению.

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

Другая неприятность может заключаться в том, что при завершении программы по Ctrl-Break вектор прерывания не будет восстановлен, если только Вы не предусмотрите, чтобы программа реакции на Ctrl-Break выполняла эту процедуру.

ПРИМЕЧАНИЕ: Не рекомендуется прямо устанавливать вектор прерываний, обходя функцию DOS. В частности в многозадачной среде операционная система может поддерживать несколько таблиц векторов прерываний и реальный физический адрес таблицы может быть известен только DOS.

Основные функции прерывания MS DOS 21h

Прерывание 21h (DOS) предназначено для предоставления программисту различных услуг со стороны DOS. Этими услугами является набор функций.

Последовательность действий при использовании этих функций:

1. Поместить номер функции в регистр AH. Если используется функция с подфункцией, то номер подфункции обычно помещается в регистр AL.

2. Поместить передаваемые функции параметры в определенные регистры (для каждой ф-ции соответствующие)

3. Вызвать прерывание командой int 21h

4. Извлечь рез-ты работы функции из определенных регистров

Основные функции ввода, вывода для прерывания 21h:

00h — завершение работы программы

01h – ожидание набора символа с последующим его эхосопровождением и с проверкой на CTRL+BREAK

На выходе в AL — ACSII-код символа.

02h – изображение (вывод) символа с проверкой на CTRL+BREAK.

На входе в DL — ACSII-код символа

05h — вывод символа на принтер.

На входе в DL — ACSII-код символа

06h – изображение символа без проверки на CTRL+BREAK.

На входе в DL — ACSII-код символа

07h – ожидание набора символа без последующего его эхосопровождения и без проверки на CTRL+BREAK

На выходе в AL — ACSII-код символа.

08h – ожидание набора символа с последующим его эхосопровождением и с проверкой на CTRL+BREAK

На выходе в AL — ACSII-код символа.

09h — изображение строки символов с проверкой на CTRL+BREAK.

На входе — DS:DX = начальному адресу строки с символом $ на конце.

0ah – чтение строки в буфер клавиатуры.

На входе DS:DX = адрес буфера с форматом:

1 байт — размер буфера для ввода (формирует пользователь)

2 байт — число фактически введенных символов (заполняет система по окончанию ввода-нажатию Enter (0dh), этот символ не считает)

3 байт и далее — введенная строка с символом 0dh на конце

На выходе — введенная строка в буфере

0bh – чтение состояния клавиатуры

На выходе AL = 0 — буфер НЕ пуст

AL = offh – буфер клавиатуры пуст

0сh – освобождение буфера клавиатуры и вызов нужной функции

На входе в AL – номер нужной функции

На выходе – регистры в соответствии с вызываемой функцией

Проверка на CTRL+BREAK означает, что если в процессе работы данной функции была нажата эта комбинация клавиш, то по умолчанию происходит прерывание 23h и выход из программы.

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Только сон приблежает студента к концу лекции. А чужой храп его отдаляет. 8807 — | 7523 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Diplom Consult.ru

Лабораторная работа № 1.

Обработка прерываний

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

Обработка прерывания

Рассмотрим подробно действия, которые называются обработкой прерывания. Эти действия выполняются независимо от того вызвано ли прерывание аппаратно или программно. При получении сигнала на прерывание (при аппаратном прерывании от программируемого контроллера прерываний, при программном командой процессора int) процессор содержимоеCS,IPи регистра флагов сохраняет в стеке. ВCSиIPпомещается адрес подпрограммы обработки прерывания, которая и выполняется, после чего восстанавливаются из стека содержимое CS, IP и регистра флагов, и процессор продолжает выполнение программы.

Двойное слово, в котором хранится адрес подпрограммы обработки прерывания, называется вектором прерывания. Всего допустимо иметь 256 различных векторов прерываний. Для хранения векторов прерываний в DOSвыделен первый килобайт памяти. Адрес вектора прерывания с номеромNвычисляется какN*4. В младшем слове хранится значениеIP, а в старшемCS.

Пример. Определить адрес прерывания 21h. Получим 21h* 4h=84h. Просмотр содержимого четырёх байтов, начиная с 0084hв сегменте 0000h

Показывает, что там храниться число

где 0DDE– адрес сегмент, а 048B– адрес смещения подпрограммы обработки прерывания с номером 21h.

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

Каждая подпрограмма обработки прерывания, в отличие от обычно подпрограммы, завершается командой iret, которая похожа на командуret, но восстанавливает из стека кромеCSиIPещё и регистр флагов.

Изменение вектора прерывания

MS-DOSпредоставляет две функции 35hи 25hпрерывания 21hдля чтения и установки вектора прерывания.

Функция 35h

Выполняет чтение адреса подпрограммы обработки прерывания.

al= номер прерывания

es:bx– указатель на подпрограмму прерывания

Примечание. Функция получает адрес, указанного ALпрерывания из таблицы векторов прерываний.BXсодержит смещение, аESсегмент адреса подпрограммы.

movah, 35h; номер функции

moval, 21h; номер прерывания

В результате: (BX) = 048B, (ES) = 0DDE.

Выполняет занесение нового вектора прерывания.

al= номер прерывания

ds:dx– указатель на подпрограмму обработки прерывания

Примечание. Функция устанавливает адрес прерывания, указанного в alв таблицу векторов прерываний.dxсодержит смещение, аdsсегмент устанавливаемой подпрограммы.

Электроника для всех

Блог о электронике

AVR. Учебный Курс. Управляемый вектор прерывания

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

Простой пример — таймер и его прерывание по переполнению.
Мы можем задавать выдержку и по прерыванию делать какие-нибудь операции. Но если в один момент времени мы хотим чтобы таймер по прерванию сделал одну операцию, а потом другую, третью. Да сколько угодно, в зависимости от состояния. А вектор один.

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

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

То есть в свитче вида:

Будет последовательное сравнение х вначале с 1, потом с 2, потом с 3 и так до перебора всех вариантов. А в таком случае реакция на Действие 1 будет быстрей чем реакция на Действие 4. Особо важно это при расчете точных временных интервалов на таймере.

Но есть простое решение этой проблемы — индексный переход. Достаточно перед тем как мы начнем ожидать прерывание предварительно загрузить в переменные (а можно и сразу в индексный регистр Z) направление куда нам надо перенаправить наш вектор и воткнуть в обработчик прерывания индексный переход. И вуаля! Переход будет туда куда нужно, без всякого сравнения вариантов.

В памяти создаем переменные под плавающий вектор:

Timer0_Vect_L: .byte 1 ; Два байта адреса, старший и младший Timer0_Vect_H: .byte 1

Подготовка к ожиданию прерывания проста, мы берем и загружаем в нашу переменную нужным адресом

CLI ; Критическая часть. Прерывания OFF LDI R16,low(Timer_01) ; Берем адрес и сохраняем STS Timer0_Vect_L,R16 ; его в ячейку памяти. LDI R16,High(Timer_01) ; Аналогично, но уже со старшим вектором STS Timer0_Vect_H,R16 SEI ; Прерывания ON

Все, можно запускать таймер и ждать нашего прерывания. С другими случаями аналогично.

А обработчик получается вида:

;============================= ; Вход в прерывание по переполнению от Timer0 ;============================= TIMER_0: PUSH ZL ; сохраняем индексный регистр в стек PUSH ZH ; т.к. мы его используем PUSH R2 ; сохраняем R2, т.к. мы его тоже портим IN R2,SREG ; Извлекем и сохраняем флаговый регистр PUSH R2 ; Если не сделать это, то 100% получим глюки LDS ZL,Timer0_Vect_L ; загружаем адрес нового вектора LDS ZH,Timer0_Vect_H ; оба байта. CLR R2 ; Очищаем R2 OR R2,ZL ; Проверяем вектор на ноль. Иначе схватим аналог OR R2,ZH ; reset’a. Проверка идет через операцию OR BREQ Exit_Tm0 ; с накоплением результата в R2 ; так мы не портим содержимое Z и нам не придется ; загружать его снова IJMP ; Уходим по новому вектору ; Выход из прерывания. Exit_Tm0: POP R2 ; Достаем и восстанавливаем регистр флагов OUT SREG,R2 POP R2 ; восстанавливаем R2 POP ZH ; Восстанавливаем Z POP ZL RETI ; Дополнительный вектор 1 Timer_01: NOP ; Это наши новые вектора NOP ; тут мы можем творить что угодно NOP ; желательно недолго — в прерывании же NOP ; как никак. Если используем какие другие NOP ; регистры, то их тоже в стеке сохраняем RJMP Exit_Tm0 ; Это переход на выход из прерывания ; специально сделал через RJMP чтобы ; Дополнительный вектор 2 ; сэкономить десяток байт на коде возврата :))) Timer_02: NOP NOP NOP NOP NOP RJMP Exit_Tm0 ; Дополнительный вектор 3 Timer_03: NOP NOP NOP NOP NOP RJMP Exit_Tm0

Реализация для RTOS
Но что делать если у нас программа построена так, что весь код вращается по цепочкам задач через диспетчер RTOS? Просчитать в уме как эти цепочки выполняются относительно друг друга очень сложно. И каждая из них может попытаться завладеть таймером (конечно не самовольно, с нашей подачи, мы же программу пишем, но отследить по времени как все будет сложно).
В современных больших осях на этот случай есть механизм Mutual exclusion — mutex. Т.е. это своего рода флаг занятости. Если какой нибудь процесс общается, например, с UART то другой процесс туда байт сунуть не смеет и покорно ждет пока первый процесс освободит UART, о чем просемафорит флажок.

В моей RTOS механизмов взаимоисключений нет, но их можно реализовать. По крайней мере сделать некоторое минимальное подобие. Полноценную реализацию всего этого барахла я делать не хочу, т.к. моей целью является удержания размера ядра на уровне 500-800 байт.
Проще всего зарезервировать в памяти еще один байт — переменную занятости. И когда один процесс захватывает ресурс, то в эту переменную он записывает время когда ориентировочно он его освободит. Время идет в тиках системного таймера которое у меня 1ms.
Если какой либо другой процесс попытается обратиться к этому же аппаратному ресурсу, то он вначале посмотрит на состояние его занятости, считает время в течении которого будет занято и уйдет покурить на этот период — загрузит сам себя в очередь по таймеру. Там снова проверит и так далее. Это простейший вариант.

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

Решение проблемы — добавление еще одной очередной цепочки, на этот раз уже на доступ к ресурсу. Чтобы он не простаивал вообще. Т.е. один выскочил, тут же второй, третий и так далее пока все процессы не справят свою нужду в какой нибудь там USART.
Недостаток очевиден — еще одна очередь это дополнительная память, дополнительный код, дополнительное время. Можно, конечно, извратиться и на очередь к вектору натравить код диспетчера основной цепи. Но тут надо все внимательно отлаживать, ведь вызываться он будет по прерыванию! Да и громоздко, требуется лишь тогда, когда у нас много желающих.

Второе решение — выкинуть переменную времени занятости, оставив только флаг «Занято!». А процесс который пытается обратиться не убегает покурить, а отскакивает на пару шагов назад — на конец очереди задач и сразу же ломится обратно. Народ вокруг сортира не вокруг бегает, а толкется локтями у входа по принципу кто первый пролезет.
Недостаток другой — большая нагрузка на главный конвеер, куча запросов на постановку в очередь так недолго распухнуть на всю оперативку и повстречаться со стеком, а это черевато глобальным апокалипсисом.

Разумеется таймер тут приведен для примера, большую часть задач можно решить системным таймером RTOS, но если нужна вдруг меньшая дискретность или высокая скорость реакции на событие (а не пока главный конвеер дотащит задачу до исполнения), то механим управляемых прерываний, ИМХО, то что доктор прописал.

51 thoughts on “AVR. Учебный Курс. Управляемый вектор прерывания”

Все же нет ничего лучше старой, прекрасной системы приоритетных прерываний. Все предельно просто, реакция мгновенная, более приоритетное событие прервет обработку прерывания от другого, потом она будет продолжена… 8 уровней приоритета. Куча режимов, задается все программно, очень гибко настраивается. Тоскую по ним уже 20 лет, с тех пор как перестал делать контроллеры на КР580ВМ80А с контроллером прерываний КР580ВН59А…
Можно было как угодно переназначать программно приоритеты, или сделать автоматическую циклическую смену, когда обработанное прерывание становится самым младшим, и еще много всего… Кстати, КР580ВН59А легко прицепить к любой меге, имеющей внешнюю шину адреса и данных, или I8048, I8051. Конечно, корпус крупноват, (DIP28), и потребление милиампер 100… И внутренние прерывания через него не пустишь, только внешние. Удивляюсь, в последнее время в контроллеры чего только не суют, а сделать систему прерываний приоритетной — западло… А ведь это намного проще, чем USART, АЦП, не говоря уж о USB. Просто немного простой логики. Как мне их не хватает… К хорошему привыкаешь быстро, но кто не пробовал, тому этого не понять, какая это была прелесть для задач реального времени… Кому интересно, почитайте о режимах и их настройке в КР580ВН59А. (Intel 8059).

Тоже удивляюсь. Ведь такой рулез был! На С51 кстати был простейший двухуровневый поллинг запросов, хоть что то. А на AVR это уже зарубили на корню :(

В MCS 48 тоже было 2 вектора — от таймера и внешнее. А вот про приоритет в них уже не помню… Помню, что чтобы не заморачиваться с сохранением в прерываниях, просто использовал для прерываний регистры другого банка, неиспользуемые в программах. Там индексные регистры для косвенной адресации тоже в обоих банках были.

Ну вложенные прерывания в AVR таки есть, так что прерывать прерывания можно.

А вот зачем нужна приоритетная система, если не требуется точность ответа на внешний раздражитель с точностью до такта, я не понимаю. Остальные случаи вполне разруливаются софтом.

Приоритетная система нужна для быстрой реакции на важные события. Она гарантирует, что наиболее приоритетные прерывания всегда будут обрабатываться оперативно, независимо от состояния программы и обработки прерываний более низкого уровня. В то же время, при окончании обработки прерывания сразу же продолжается обработка прерывания более низкого уровня, если оно было прервано.
Это позволяет делать обработчики прерываний низких приоритетов более обьемистыми, не экономя каждый байт, не боясь ухудшить реакцию на прерывания более высоких уровней.
Конечно, будучи изначально лишенным этих возможностей, трудно их оценить. Но попробовав, отказаться трудно. Это как осциллограф для электронщика: пока не пользовался — вроде и ни к чему, попользовавшись — уже без него и не представляешь себе нормальную работу. Конечно, если прерываний всего используешь 2-3, да и время реакции не важно (ну, тикнул таймер, а потом хоть миллисекунду его обрабатывай), приоритетные прерывания мало что изменят. Но если их хотя бы штук 5, и некоторые из них ждать долго не могут, тогда выигрыш очень велик.

> Она гарантирует, что наиболее приоритетные прерывания всегда будут обрабатываться оперативно, независимо от состояния программы и обработки прерываний более низкого уровня.
Как я уже сказал, AVR умеет вкладывать прерывания. Этого достаточно для организации описанного поведения, ибо время реакции в любом случае будет определяться длиной максимальной критической секции (нам же неинтересно, если нас прервут например, во время сохранения SREG), а она зависит более от структуры программы, чем от аппаратуры.

Более того, даже требование вкладываемости прерываний не является обязательным, ибо давным давно известна техника деления обработчика прерываний на top (непрерываемую) и bottom (прерываемую) поповины. Достаточно только уметь переключать контексты и выполнение нижних половинок можно переложить на шедулер.

При желании можно вообще обойтись без прерываний (постоянно проверяя их источники). Я же говорил, не попробовав — не поймешь. Это как описывать оттенки цвета слепому…

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

Или эмулировать приоритеты…
Конечно, это лишний код и такты, но…
Например отдать под флаг приоритета один регистр или ячейку оперативки.
А в каждый обработчик добавить сравнение:
Timer0_Overflow.
if r20>05 then goto exit \\сравниваем флаг приоритета с нашим приоритетом(05). Если работает более приоритетное прерывание, не мешаем ему, выходим. Можно > заменить на >=
push r20 \\Сохраним приоритет прерванного прерывания
r20:=05 \\пишем во флаг свой приоритет(чтоб и нас могли прервать еще более важные прерывания)
CLI \\разрешим нас прервать
код обработчика \\работаем
SEI \\запретим нас прерывать(а то не сможем правильно восстановить прерванное прерывание)
pop r20 \\восстановим приоритет прерванного прерывания
if r20>0 then goto exit \\Если мы кого-то прервали, выходим
r20:=00 \\Иначе, если мы никого не прервали, очистим флаг
exit: reti \\и выйдем

Недостатки есть конечно:
Все прерывания с более низким приоритетом при работе более приоритетного не откладываются, а завершаются — это главный недостаток…
Прерывать мы будем даже более приоритетные прерывания, но это его замедлит всего на несколько тактов…

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

И всё-таки сделали трех уровневую систему прерываний. Xmega — новое детище атмела. Да и появились ещё кое-какие полезные вещички. Правда по какой цене и где покупается… :) Но, главное, есть! где-то.
http://atmel.com/dyn/resources/prod_documents/doc8068.pdf стр 24

Лучше поздно, чем никогда… Правда, Xmega для меня (для реально возможных у меня применений) несколько избыточна… все, для чего не хватит Меги 128, проще переложить на нормальный комп, обеспечив лишь канал связи.

Я сейчас работаю с СС2510 (Chipcon). Приоритеты прерываний имеются. Только заданы они жестко. Не уверен насчет того же в MSP430 — писал под него достаточно болшой софт, но там приоритеты прерываний (как ни странно) не понадобились

по моему не
LDI ZL,Timer0_Vect_L ; загружаем адрес нового вектора
LDI ZH,Timer0_Vect_H ; оба байта.

Перехват прерываний и создание резидентных программ

Вначале вспомним, что такое прерывания и как устроена система прерываний нашего процессора.

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

При любом прерывании выполняется следующая последовательность действий:

· Процессор автоматически сохраняет в стеке адрес возврата, то есть адрес той команды фоновой программы, на которую мы должны будем вернуться из обработчика. Для этого процессор заталкивает в стек содержимое трех регистров: f(регистр флагов), cs и ip. Пара cs:ip как раз и задает адрес возврата.

· После того, как адрес возврата сохранен, процессор загружает в регистры cs и ipадрес первой команды обработчика, который он берет из таблицы прерываний (смотри ниже), тем самым, передавая управление обработчику.

· В конце обработчика программист пишет команду iret(возврат из прерывания). По этой команде процессор выталкивает из стека адрес возврата (в регистры csи ip) и флаги (в регистр f). Тем самым происходит возврат в фоновую программу. Естественно, этот возврат будет правильным, только если вершина стека в момент выполнения команды iretнастроена надлежащим образом, иначе в наши регистры вытолкнется «мусор» и компьютер зависнет.

Открытым остался вопрос, а откуда процессор берет начальный адрес обработчика? В процессорах фирмы Intel каждому прерыванию присвоен номер (чаще говорят, тип). Тип прерывания может лежать в диапазоне 0 – 255, то есть всего возможно 256 различных прерываний. Реально в компьютерах задействованы не все 256 прерываний, но для задействованного прерывания обязательно должен быть обработчик, расположенный в известном месте памяти. Для того чтобы по известному типу процессор мог определить начальный адрес обработчика, в младшем килобайте памяти (адреса 00000h – 003ffh) создается таблица прерываний. В этой таблице последовательно записаны адреса обработчиков для различных типов (начиная с типа 0). При этом каждый такой адрес задается в виде пары сегмент:смещение(сегментзагружается в cs, смещение– в ip). Такая пара однозначно задает адрес ячейки памяти, в которой располагается первая команда обработчика прерывания данного типа. Обычно такую пару называют вектором прерывания. Формат таблицы прерываний показан на рис 3.3. Здесь displи disphсоответственно младший и старший байты поля смещение, а segl и segh— младший и старший байты поля сегмент.Таким образом, любой вектор занимает в памяти 4 байта. Для того чтобы по типу найти адрес вектора, надо этот тип умножить на 4. Например, тип = 2, адрес вектора прерываний для этого типа равен 2*4 = 8, то есть искомый вектор располагается в ячейках памяти 8, 9, 10 и 11.

Пусть, например, у нас в памяти (в таблице прерываний) такая ситуация:

адрес содержимое
00004h 22h
00005h 07h
00006h 91h
00007h c0h

тогда пара c091:0722hпредставляет собой вектор для прерывания типа 1, а начальный адрес обработчика для этого типа получается из этой пары так:

A = (c091h)*16 + 0722h = c0910h + 0722h = c1032h.

Обратный пример. Пусть мы написали обработчик для прерывания типа 2 и расположили его в памяти, начиная с адреса 55a60h. Как нам преобразовать этот адрес в вектор? Надо представить этот адрес в виде пары, а это неоднозначная операция, то есть, как правило, существует несколько правильных пар, задающих один и тот же адрес. Например, наш адрес можно представить в виде пары 55a6:0000h(или 55a0:0060h или….). Теперь надо записать вектор в таблицу:

адрес содержимое
00008h 00h
00009h 00h
0000ah a6h
0000bh 55h

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

Илон Маск рекомендует:  Работа с массивами в delphi

Для того чтобы резидентная программа имела возможность отслеживать «свое событие», она должна перехватывать соответствующее прерывание. Например, при реакции на определенные клавиши наиболее удобно перехватывать аппаратное прерывание от клавиатуры, которому в системе присвоен тип 9. При активации резидентной программы через заданные промежутки времени можно перехватывать аппаратное прерывание от таймера – тип 8 (лучше не тип 8, а тип 1сh).

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

Посмотрим, что происходит, когда мы нажимаем какую-то клавишу на клавиатуре. Контроллер клавиатуры выставляет СКЭН – код нажатой клавиши в порт 60h и формирует запрос на контроллер прерываний. Последний передает этот запрос на процессор и сообщает процессору тип данного прерывания (тип 9). Обработчик 9-го прерывания читает порт 60h, переводит СКЭН – код в ASCII – код (если нажата символьная клавиша, для функциональных клавиш ASCII – код = 0) и помещает и СКЭН и ASCII коды в кольцевой буфер клавиатуры, расположенный в области переменных BIOS. Адрес элемента буфера, в который была записана информация о нажатой клавише, храниться в ячейке памяти с адресом 0041ah. Прикладные программы и операционная система считывают информацию о нажатых клавишах уже из этого буфера (подробно буфер клавиатуры описан в Приложении). Для этого они либо используют соответствующие сервисные прерывания (например, int 16h), либо работают с буфером напрямую:

; читаем из буфера информацию о нажатой клавише

Mov ax, 40h

mov es, ax;начальный адрес сегмента 00400h

mov bx, es:[1ah]; в bx смещение (относительно es) ячейки, в которой
; записана информация о клавише

mov ax, es:[bx] ; теперь в ah– СКЭН, а в al– ASCII коды

Отметим еще один нюанс, с которым Вам, возможно, придется столкнуться. При нажатии клавиши происходитне одно прерывание, а два. Первое прерывание возникает, когда мы эту клавишу нажимаем, второе – когда отпускаем. И в том и в другом случае контроллер клавиатуры выставляет в порт 60h соответственно СКЭН – код нажатия и СКЭН – код отжатия, после чего вызывается обработчик 9-го прерывания. Для любой клавиши справедливо:

Код отжатия = код нажатия + 128

иначе говоря, код отжатия всегда характеризуется наличием единицы в старшем разряде. Например, код нажатия клавиши 506h, код отжатия – 86h, код нажатия клавиши «стрелка — вверх» на цифровой клавиатуре – 48h, отжатия – c8h. Иногда такая ситуация начинает мешать и, для правильной работы резидента, код отжатия приходится отсекать.

Примечание:для некоторых функциональных клавиш, появившихся на расширенной клавиатуре (101 клавиша и более), СКЭН – код (как нажатия, так и отжатия) представляет собой последовательность байт (два байта, а иногда (если, например, включен NUM LOCK) и четыре). Например, когда мы нажимаем клавишу «стрелка – вверх», расположенную не на цифровой клавиатуре, возникают два прерывания и код нажатия имеет вид e0 48h, также два прерывания возникает, и когда мы эту клавишу отпускаем, а код отжатия имеет вид 0e c8h. При включенном режиме NUM LOCK, код нажатия этой клавиши имеет вид e0 2a e0 48h, а код отжатия – e0 c8 e0 aah(и при нажатии и при отжатии возникают по четыре прерывания). Эту ситуацию тоже иногда приходится учитывать.

Обработчик прерывания типа 8 обрабатывает прерывания от 0-го канала таймера, который отведен для службы системного времени. Таймер тикает примерно 20 раз в секунду и по каждому тику обработчик прибавляет единицу к системным часам, расположенным в области переменных BIOS. Пользователю не рекомендуется перехватывать 8-е прерывание. Специально для пользователя в конце стандартного обработчика 8-го прерывания стоит командаint 1ch. Обработчик этого программного прерывания по сути дела фиктивен, поскольку состоит из единственной команды iret. При работе с таймером пользователю как раз и рекомендуется перехватывать прерывание 1ch.

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

· функция 35h прерывания 21h

Входные параметры:в al– тип перехватываемого прерывания.

Возвращает в паре регистров es:bx вектор для прерывания, тип которого задан в al.

· функция 25h прерывания 21h

Входные параметры:в ds:dx– новый вектор, который мы записываем в таблицу, в al – тип прерывания, задающий место в таблице, куда производится запись вектора.

Определить, что заносить в ds и в dx в качестве вектора достаточно просто. В dsдолжен быть начальный адрес сегмента памяти, в который загружен наш резидент, а так как, при запуске программы на выполнение, DOS именно так и настраивает ds, в этом регистре и так находится правильная информация, перенастраивать его не надо. В dxдолжно находиться смещение первой команды нашего резидента. Для того чтобы определить это смещение, достаточно поставить на эту команду метку (например, met:) и написать команду mov dx, offset met. После этого в dx будет нужное значение.

Таким образом, для перехвата прерывания надо выполнить примерно следующую последовательность действий:

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

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

После этого при возникновении соответствующего прерывания вместо системного обработчика будет вызвана наша резидентная программа. Последняя обычно определяет, касается ли ее произошедшее событие или нет (например, нажата ли нужная комбинация клавиш). Если событие «наше» резидент производит требуемую обработку, если «не наше» — передает управление системному обработчику, адрес которого мы сохранили.

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

Обычно резидентная программа пишется в СОМ формате и имеет структуру, показанную на рис 3.4.

PSP
область данных резидентной части
РЕЗИДЕНТНАЯ ЧАСТЬ
ЗАГРУЗОЧНАЯ ЧАСТЬ
область данных загрузочной части

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

Завершить работу программы, оставив ее (или ее часть) в памяти, можно, например, с помощью прерывания int 27h. Входным параметром для этого прерывания является размер (в байтах) оставляемой в памяти части программы (начиная с начала программы, то есть с PSP). Этот входной параметр задается в dx. Определить размер оставляемой части очень просто. Достаточно поставить метку на первую команду загрузочной части (например, start:) и написать команду mov dx, offset start.

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

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

Последовательность действий в загрузочной части может быть примерно следующей:

1. Считать из таблицы прерываний старый вектор (функция 35h прерывания 21h);

2. Произвести проверку на повторную установку, при положительном результате перейти к пункту 6;

3. Сохранить старый вектор в известном (резиденту!!) месте памяти;

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

5. Завершить программу, оставив ее резидентной (прерывание 27h);

6. Вывести сообщение о том, что программа уже установлена, и завершить программу, ничего не оставляя в памяти (функция 4ch прерывания 21h).

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

Схема 1.Сохраняем (в стеке) значения всех регистров фоновой программы, в том числе и сегментных, которые портит наш резидент. Если этого не сделать фоновая программа, при возврате, получит испорченное содержимое регистров и вряд ли будет работать корректно. Выполняем требуемую обработку. Восстанавливаем значения регистров из стека (в обратном порядке!). Передаем управление системному обработчику (командой jmp far), адрес которого нам сохранила загрузочная часть. Системный обработчик впоследствии сам вернет управление фоновой программе (командой iret).

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

Pushf

Call far

Первая команда заталкивает в стек содержимое регистра флагов, вторая –вызывает системный обработчик как подпрограмму. (Возврат из этой «подпрограммы» происходит по команде iret, которая выталкивает из стека адрес возврата и флаги! Именно поэтому нужна команда pushf.) При возврате из системного обработчика управление снова получает наш резидент. Он выполняет требуемые действия, восстанавливает регистры и возвращает управление фоновой программе (командой iret).

Схема 3.Сохраняем значения регистров. Выполняем требуемую обработку. Восстанавливаем регистры. Возвращаем управление фоновой программе (командой iret). То есть в этой схеме мы полностью игнорируемсистемный обработчик и, следовательно, должны работать «за него». В частности, если мы перехватываемаппаратное прерывание (от таймера, от клавиатуры и. т. д.) мы должны не забыть снять «штору», которую ставит контроллер прерываний, послав число 20hв порт 20h. То есть здесь мы должны знать и учитывать все нюансы работы аппаратуры. Именно поэтому эта схема на практике используется редко.

Компилируя три рассмотренные выше схемы между собой можно получить более сложные схемы действий резидентной части.

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

К чему это может привести? Пусть в области данных резидентной части нашей программы выделен байт под переменную, которую мы назвали flag. Пусть при написании резидентной части программист решил присвоить этой переменной значение 5, для чего написал команду:

Mov flag, 5


Программист допустил (скорее всего) грубую ошибку. Ведь в этой команде по умолчанию начальный адрес сегмента задает содержимое регистра ds, а оно, как мы помним, пришло из фоновой программы и указывает на фоновую программу. То есть реально наша пятерка запишется не в переменную flag, а в какую-то ячейку памяти фоновой программы. Велика вероятность, что после этого фоновая программа просто перестанет корректно работать и зависнет (после нашего возврата в эту фоновую программу). А как же нам правильно обратиться к нашей переменной? Например, это можно сделать так:

Mov cs:flag, 5

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

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

Далее приводятся примеры двух тривиальных резидентных программ. Обе программы делают одно и тоже: отслеживают нажатие комбинации клавиш ALT/t и выводят сообщение об этом «событии» на экран. Однако первая программа написана по схеме 1, а вторая по схеме 2. Приведем данные, необходимые для понимания этих программ:

· если нажата клавиша ALT, бит 3 в ячейке памяти 00417h установлен в единицу;

· скэн-код клавиши t равен 14h, ASCII равен 74h;

· скэн-код комбинации клавиш ALT/t равен 14h, ASCII равен .

ПРОГРАММА 1.Перехватывает аппаратное прерывание от клавиатуры (тип 9). Реализована схема 1. Необходимо учитывать, что программа предназначена для работы в текстовом режиме. Например, в графическом (не полноэкранном) режиме FAR она может работать некорректно.

Code segment

Assume cs:code,ds:code

Org 100h

; Переход на загрузочную часть программы

F10: jmp start

; Область данных резидентной части.

key dw 5555h; ключ расположен в ячейке

; со смещением 103h (100h байт

; PSP и 3 байта jmp start)

soob db ‘нажата Alt/t’

oldvect dd 0; здесь запоминаем адрес

; Здесь начинается резидентная часть программы. Очень важным является

; то, что когда мы попадаем сюда из фоновой программы, все сегментные

; регистры, кроме CS, содержат данные фоновой программы. Поэтому там,

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

; необходимо использовать префикс замены сегмента CS:.

newvect:

; Сохраняем в стеке все регистры, которые можем испортить

Push ax

Push es

Push bx

Push cx

push dx
push si

; Выясняем, нажата ли ALT/t

in al, 60h
cmp al, 14h
; нажата t?
jne exit

mov ax, 40h
mov es, ax
mov al, es:[17h]
and al, 1000b
; нажата ALT?
jz exit

; Запоминаем позицию курсора (в SI).

Mov ah, 3

Mov bh, 0

int 10h
mov si, dx

; Выводим сообщение, начиная с текущей позиции курсора, что Alt/t нажата.

Mov cx, 12

Mov bx, offset soob

M1: mov ah, 0eh

mov al, cs:[bx]

Int 10h

Inc bx

Loop m1

; Вводим задержку секунд на 5, чтобы полюбоваться надписью.

Mov ah, 0

Int 1ah

Mov bx, dx

Add bx, 50

M2: mov ah, 0

Int 1ah

Cmp bx, dx

Ja m2

; Восстанавливаем курсор в старой позиции. Старые координаты берутся из SI.

Mov dx, si

Mov ah, 2

Mov bh, 0

Int 10h

; Стираем нашу надпись пробелами. Курсор при этом не смещается,

; а остается в нужной (старой) позиции.

Mov ah, 0ah

mov al, ‘ ‘

Mov cx, 12

Mov bh, 0

Int 10h

exit:

; Восстанавливаем в регистрах информацию фоновой программы.

Pop si

Pop dx

Pop cx

Pop bx

Pop es

Pop ax

; Вызываем правильный системный обработчик. Так как ячейка oldvect

; описана как двойное слово, команде jmp автоматически будет присвоен тип far.

Jmp cs:oldvect

; Здесь кончается резидентная часть и начинается загрузочная часть программы.

start:

; Получаем вектор правильного (системного) обработчика (в ES:BX).

Mov ah, 35h

Mov al, 9

Int 21h

; Производим проверку (сравнивая с ключом) на повторную установку программы.

cmp word ptr es:[103h], 5555h

Jz inst

; Запоминаем вектор правильного (системного) обработчика.

Mov word ptr oldvect, bx

mov word ptr oldvect+2, es

; Устанавливаем вектор своего обработчика.

Mov dx, offset newvect

Mov ah, 25h

Mov al, 9

Int 21h

; Завершаем программу, оставляя резидентной в памяти ее часть,

; от начала PSP до метки start.

Mov dx, offset start

Int 27h

; Если программа уже установлена в памяти, выдаем сообщение об

; этом и завершаем программу, ничего не оставляя в памяти.

Inst: mov ah, 9

Mov dx, offset soob2

Int 21h

Mov ah,4ch

Int 21h

; Область данных загрузочной части.

soob2 db ‘Программа уже установлена$’

Code ends

End f10

ПРОГРАММА 2.Перехватывает прерывание BIOS int 16h. Это прерывание возвращает в axиз буфера клавиатуры СКЭН и ASCII коды нажатой клавиши. Основная ветвь резидентной части программы реализует схему 2. То есть, попадая в наш резидент, мы сразу вызываем стандартный обработчик int 16h. Этот обработчик возвращает нам в ax код клавиши, с которым мы и работаем. Поскольку при этом возврат управления фоновой программе осуществляет наш резидент, он и должен обеспечить передачу (в регистре ax) полученного кода клавиши фоновой программе. В резидентной части программы имеется побочная ветвь, реализующая схему 1. Связано это с тем, что прерывание int 16hимеет целый ряд функций, из которых нам интересны только две: и 10h. Остальные функции как раз и отсекаются этой побочной ветвью.

Code segment

Assume cs:code,ds:code

Org 100h

; Переход на загрузочную часть программы

F10: jmp start

; Область данных резидентной части.

key dw 5555h; ключ расположен в ячейке

; со смещением 103h (100h байт

; PSP и 3 байта jmp start)

soob db ‘нажата Alt/T’

oldvect dd 0; здесь запоминаем адрес

; Здесь начинается резидентная часть программы. Очень важным является

; то, что когда мы попадаем сюда из фоновой программы, все сегментные

; регистры, кроме CS, содержат данные фоновой программы. Поэтому там,

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

; необходимо использовать префикс замены сегмента CS:.

newvect:

; Сохраняем в стеке все регистры, которые можем испортить

Push es

Push bx

Push cx

push dx
push si
push di

; Это функция ?

cmp ah, 0
je resid

; Это функция 10h?

cmp ah, 10h
jne exit1; если «не наши» функции, отдаем управление системному
; обработчику

resid:

; Вызываем стандартный обработчик

pushf
call cs:oldvect

; Если в AHвернулось 14h,а в AL -это код Alt/t.

Cmp ax, 1400h

jne exit
mov di, ax; сохраняем код клавиши, чтобы вернуть его фоновой программе

; Запоминаем позицию курсора (в SI).

Mov ah,3

Mov bh,0

int 10h
mov si, dx

; Выводим сообщение, начиная с текущей позиции курсора, что Alt/t нажата.

Mov cx,12

Mov bx,offset soob

M1: mov ah,0eh

mov al,cs:[bx]

Int 10h

Inc bx

Loop m1

; Вводим задержку секунд на 5, чтобы полюбоваться надписью.

Mov ah, 0

Int 1ah

Mov bx, dx

Add bx, 50

M2: mov ah, 0

Int 1ah

Cmp bx, dx

Ja m2

; Восстанавливаем курсор в старой позиции. Старые координаты берутся из SI.

Mov dx, si

Mov ah,2

Mov bh,0

Int 10h

; Стираем нашу надпись пробелами. Курсор при этом не смещается,

; а остается в нужной (старой) позиции.

Mov ah,0ah

mov al,’ ‘

Mov cx,12

Mov bh,0

Int 10h

mov ax, di; восстанавливаем код клавиши, который надо вернуть фоновой
; программе

exit:

; Восстанавливаем в регистрах информацию фоновой программы.

Pop di

Pop si

Pop dx

Pop cx

Pop bx

Pop es

; Возвращаем управление фоновой программе.

Iret

; Возвращаем управление стандартному обработчику

exit1:
pop di

Pop si

Pop dx

Pop cx

Pop bx

Pop es

Jmp cs:oldvect

; Здесь кончается резидентная часть и начинается загрузочная часть

start:

; Получаем вектор правильного (системного) обработчика (в ES:BX).

Mov ah,35h

Mov al, 16h

Int 21h

; Производим проверку (сравнивая с ключом) на повторную установку программы.

cmp word ptr es:[103h],5555h

Jz inst

; Запоминаем вектор правильного (системного) обработчика.

Mov word ptr oldvect,bx

mov word ptr oldvect+2,es

; Устанавливаем вектор своего обработчика.

Mov dx,offset newvect

Mov ah,25h


Mov al, 16h

Int 21h

; Завершаем программу, оставляя резидентной в памяти ее часть,

; от начала PSP до метки start.

Mov dx,offset start

Int 27h

; Если программа уже установлена в памяти, выдаем сообщение об

; этом и завершаем программу, ничего не оставляя в памяти.

Inst: mov ah,9

Mov dx,offset soob2

Int 21h

Mov ah,4ch

Int 21h

; Область данных загрузочной части.

soob2 db ‘Программа уже установлена$’

Code ends

End f10

Неприятной особенностью многих резидентных программ является их критичность к операционной среде, в которой они запускаются. Зачастую резидент, успешно работающий в Volkov commander, не работает в DOS Navigator и наоборот. Это связано с тем, что мы не знаем или не учитываем специфику работы конкретной среды.

В заключение этого раздела по традиции приведем варианты заданий к лабораторной работе.

ЗАДАНИЯ К ЛАБОРАТОРНОЙ РАБОТЕ

1. Через временной интервал (например, 10 секунд) на экран выводится какое-либо сообщение. Через 10-20 секунд сооб­щение с экрана снимается, и работа ПЭВМ продолжается обычным обра­зом.

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

3. При нажатии клавиши ENTER в центр экрана выводится сооб­щение: «Отдыхаю, подождите минутку». Через 10-20 секунд сообщение снимается, и работа ПЭВМ продолжается обычным образом.

4. ПЭВМ реагирует на клавишу «стрелка — вверх» как на клавишу «стрелка — вниз» (и наоборот), на клавишу «стрелка — влево» как на клавишу «стрелка — вправо» (и наоборот).

5. При нажатии клавиши F1 программа очищает экран и безоста­новочно выводит на экран сообщение «Не хочу вам помогать!», прокручивая при этом экран вверх. Секунд через 10-20 этот процесс прекращается, восстанавливается экран, и работа ПЭВМ продолжается обычным образом.

6. Через равные промежутки времени (например 30 секунд) ре­зидент блокирует / разблокирует клавиатуру.

7. При нажатии клавиши Т сообщается текущее системное время.

8. Нажатие клавиши D замедляет / восстанавливает реакцию сис­темы на нажатие клавиш клавиатуры. Замедление реакции должно быть достаточным, для того чтобы его можно было заметить визуально.

9. При нажатии клавиши R очищается правая, а при нажатии L — левая половина экрана. Через 10-20 секунд после нажатия любой из этих клавиш экран восстанавливается.

10. Нажатие клавиши G меняет размер курсора. (Как правило, любая среда, в которой мы работаем, например, Dos Navigator, при возврате ей управления восстанавливает размер курсора. То есть, чтобы гарантированно увидеть наше изменение курсора, можно, например, запускать нашу программу в Command.com).

11. Нажатие клавиши I инвертирует цвета экрана.

12. После установки резидента система перестает реагировать на нажатие клавиши F7. На другие клавиши система реаги­рует обычным образом.

Работа со звуком.

В состав любой ПЭВМ фирмы IBM входит микросхема таймера i8254. Схема ее использования в ПЭВМ представлена на рис. 3.5.

Микросхема таймера имеет три канала, соответственно каналы 0,1 и 2. Канал 0 отводится для службы системного времени, канал 1- для регенерации памяти, а канал 2- для управления работой динамика.

Порты таймера имеют следующие системные адреса:

канал 0 — 40h;

канал 1 — 41h;

канал 2 — 42h;

регистр управляющего слова (РУС) — 43h.

Через РУС производится настройка каналов, через остальные порты -загрузка/считывание информации в/из соответствующих каналов.

Для работы со звуком нам нужен канал 2 и РУС. Каналы 0 и 1 перезагружать и перенастраивать категорически запрещено, так как система может выйти из строя (во всяком случае, до перезапуска).

Работа канала 2 заключается в том, что он делит опорную частоту (fоп=1.19. МГц.) на коэффициент пересчета Кпр, который заранее загружается в канал. Получаемая fвых=fоп/Кпр подается на динамик. Надо учитывать, что работой канала 2 и подачей fвыхна динамик управляют два младших бита порта 61h. Если бит 0 порта 61h равен единице, то работа канала 2 (счет) разрешается. Если бит 1 порта 61h равен единице, то разрешается подача fвых на динамик. Таким образом, звук будет воспроизводиться, только если оба этих бита установлены в единицу.

Коэффициент пересчета для любой ноты можно определить исходя из выражения:

Кпр=fоп/fноты.

При этом учитываются следующие соотношения:

частота ноты «до» 1-й октавы (fдо) = 32.625 Гц;
частота ноты «до» 2-й октавы = 2*fдо;
частота ноты «до» 3-й октавы = 4*fдо;
и так далее;

частота «до-диез» = а*fдо;

частота «ре» = а*fдо-диез;

где а =1.06 (приблизительно).

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

а) коэффициент пересчета и время звучания для каждой ноты;

б) длительности всех пауз между нотами.

После того как все эти числа известны, надо последовательно выполнять следующие действия:

1. Запретить звучание, для чего установить в ноль оба младших бита порта 61h (не меняя, при этом, остальные биты этого порта);

2. Настроить канал 2, выполнив две команды:

; настраиваем канал 2 на передачу двух байт Кпр., на режим 3 и на
; двоичный счет

mov al, 0b6h
9 10Следующая ⇒

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰).

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

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим.

Dos fn 35h: дать вектор прерывания

Связь с администрацией сайта:

Среди толпы я одинок

  • Вы здесь:
  • Home >
  • Блог >
  • Документация и курсы >
  • Самоучитель по ассемблеру >
  • Глава 18 — практика: работа с файлами, перехват и восстановление прерываний

Глава 18 — практика: работа с файлами, перехват и восстановление прерываний

  • Written by Administrator
  • Tagged under АссемблерAssemblerСамоучитель по ассемблеру
  • Print

Замечание! Это пожалуй самая большая глава! информация представленная в ней в принципе довольно важна! К сожалению при работе в NT/Windows 2000/XP вам не удастся опробовать пример! Тут нужен чистый DOS, или хотя бы Win9x! Я например воспользуюсь эмулятором!

Работа с файлами через описатели

Если программы, написанные на языках высокого уровня могут открыть файл без выполнения подготовительных действий (они выполняются автоматически), то ассемблерные программы должны создать специальные области данных, которые используются при операциях ввода/вывода. Используется два метода доступа к файлам: метод управляющего блока файла (FCB) и метод дескриптора файла. С помощью метода FCB можно получить доступ только к файлам, находящимся в текущем каталоге. Метод дескриптора файла позволяет получить доступ к любому файлу, независимо от того, какой каталог является текущим.
Начиная с DOS версии 2.0, в набор функций прерывания 21h включе-ны UNIX-подобные файловые функции. Идея их состоит в том, что, когда программа открывает файл, DOS возвращает 16-битовое значение «описателя файла» (дескриптора файла) (handle). После этого, когда программа читает, позиционирует, пишет или закрывает файл, она ссылаетесь на него через описатель. Одно из самых больших удобств – то, что можно обращаться к некоторым устройствам так, как будто это дисковые файлы, через зарезервированные описатели DOS:

Ниже приведен перечень наиболее часто используемых функций пре-рывания 21h для работы с файлами через описатели.
Функция 3cH

Создать файл.
Вход. AH=3ch
DS:DX=адрес строки ASCIIZ с именем файла
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет.
Описание. Файл создается в указанном (или умалчиваемом) оглав-лении и открывается в режиме доступа «чтение/запись». Если файл уже существует, то при открытии файл усекается до нулевой длины. Если атрибут файла – «только чтение», открытие отвергается (атрибут можно изменить функцией 43H).
Функция 5bH
Создать новый файл (не должен существовать).
Вход. AH=5bh
DS:DX=адрес строки ASCIIZ с именем файла
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
Описание. Этот вызов идентичен функции 3ch, с тем исключением, что он вернет ошибку, если файл с заданным именем уже существует.
Функция 5aH
Создать уникальный файл.
Вход. AH=5ah
DS:DX=адрес строки ASCIIZ с путем (заканчивается \)
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
DS:DX (не изменяется) становится полным
ASCIIZ-именем нового файла.
Описание. Открывает (создает) файл с уникальным именем в оглав-лении, указанном строкой ASCIIZ, на которую указывает DS:DX. Описа-ние пути должно быть готово к присоединению в его конец имени файла. Необходимо обеспечить минимум 12 байт в конце строки. После возврата строка DS:DX будет дополнена именем файла. DOS создает имя файла из шестнадцатеричных цифр, получаемых из текущих даты и времени. Ес-ли имя файла уже существует, DOS продолжает создавать новые имена, пока не получит уникальное имя.
Функция 3dH
Открыть файл.
Вход. AH=3dh
DS:DX=адрес строки ASCIIZ с именем файла
AL=режим открытия
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
Описание. В момент открытия файл должен существовать. Файл открывается в выбранном режиме доступа (AL = 0 – для чтения; AL = 1 – для записи; AL = 2 – для чтения и записи) и указатель «чтения/записи» ус-танавливается в 0.
Функция 3eH
Закрыть файл.
Вход. AH=3eh
BX=описатель файла
Выход. AX= код ошибки, если CF установлен
Описание. BX содержит описатель файла (handle), возвращенный при открытии. Файл, представленный этим описателем, закрывается, его буфер сбрасываются, а оглавление обновляется корректными размером, временем и датой.
Функция 41H
Удалить файл.
Вход. AH=41h
DS:DX=адрес строки ASCIIZ с именем файла
Выход. AX=код ошибки, если CF установлен
Описание. Имя файла не может содержать обобщенные символы («?» и «*»). Файл удаляется из заданного оглавления заданного диска. Если файл имеет атрибут только чтение, то перед удалением необходимо изме-нить этот атрибут через функцию 43H.
Функция 42H
Установить указатель чтения/записи (можно также узнать размер файла).
Вход. AH=42h
BX=описатель файла
CX:DX=смещение указателя: (CX * 65536) + DX
AL=0 переместить к началу файла + CX:DX
AL=1 переместить к текущей позиции + CX:DX
AL=2 переместить к концу файла — CX:DX
Выход. AX=код ошибки, если CF установлен
DX:AX=новая позиция указателя файла (если нет
ошибки)
Описание. Перемещает логический указатель чтения/записи к нужному адресу, с которого начнется очередная операция чтения или за-писи. Вызов с AL=2, CX=0, DX=0 возвращает длину файла в DX:AX. DX здесь старшее значащее слово: действительная длина (DX * 65536) + AX.
Функция 3fH
Читать из файла/устройства.
Вход. AH=3fh
BX=описатель файла
DS:DX=адрес буфера для чтения данных
CX=число считываемых байт
Выход. AX=код ошибки, если CF установлен
AX=число действительно прочитанных байт
Описание. CX байт данных считываются из файла или устройства с описателем, указанным в BX. Данные читаются с текущей позиции указателя чтения/записи файла и помещаются в буфер вызывающей програм-мы, адресуемый через DS:DX.
Всегда необходимо сравнивать возвращаемое значение AX (число прочитанных байт) с CX (запрошенное число байт):
1) если AX=CX, (и CF сброшен) – чтение было корректным без ошибок;
2) если AX=0 – достигнут конец файла (EOF);
3) если AX
Функция 40H
Писать в файл/устройство.
Вход. AH=40h
BX=описатель файла
DS:DX=адрес буфера, содержащего данные
CX=число записываемых байт
Выход. AX=код ошибки, если CF установлен
AX=число действительно записанных байт
Описание. CX байт данных записывается в файл или на устройство с описателем, заданным в BX. Данные берутся из буфера, адресуемого через DS:DX и записываются, начиная с текущей позиции указателя чтения/записи файла. Необходимо всегда сравнивать возвращаемое значение AX (число записанных байт) с CX (запрошенное число байт для записи): если AX = CX, запись была успешной; если AX
Некоторые функции в качестве параметра используют атрибут файла. Атрибут — это один байт битовых флагов, связанный с каждым файлом и находящийся в элементе оглавления для файла. В атрибуте определены следующие биты:
x x A D V S H R
R- только чтение (нельзя обновлять или удалять);
H- скрытый;
S- системный;
V- метка тома;
D- элемент подоглавления;
A- архивный;
x- не используются.
ASCIIZ строка, содержащая имя файла, имеет вид:

Илон Маск рекомендует:  Изменить форму комментирования WordPress

«‘d:\путь\имя_файла’,0»

Если диск и/или путь опущены, они принимаются по умолчанию. После вызова функции описатель файла должен быть сохранен для последующих операций. Количество описателей в системе регламентируется файлом CONFIG.SYS.
Приведенный ниже пример иллюстрирует процесс работы с файлом через описатели в ассемблерной программе.

;Создание файла
MOV AH,3CH
MOV CX,0
LEA DX,BUF ;DS:DX – адрес ASCIIZ строки с именем
INT 21H
JC NO_CREATE ;Проверка флага переноса
. . . ;Работа с файлом
NO_CREATE:
. . .
BUF DB ‘d:\Users\1.txt’,0

Работа с файлами через DTA

Как было сказано ранее, используются два метода доступа к файлу: метод управляющего блока файла (FCB) и метод дескриптора файла. В любом случае программа при работе с файлами должна указывать место в памяти, куда будут помещаться принимаемые данные или откуда будут извлекаться выводимые. Обычно временный буфер устанавливается раз-мером в одну запись и бывает удобно описать его как строковую переменную в сегменте данных.
Буфер, используемый методом FCB доступа к файлам, называется областью обмена с диском или DTA. На этот буфер указывает условный указатель, который хранится операционной системой и который может быть изменен программой. В документации этот указатель на DTA часто сам называют DTA. Указатель на DTA устанавливается специальной функцией DOS и после того как он установлен все функции чтения/записи автоматически обращаются к нему. Это означает, что сами функции не должны содержать адрес временного буфера.
Для установки указателя на DTA используется функцию 1AH прерывания 21H (DS:DX должны указывать на первый байт DTA). Функция 2FH прерывания 21H сообщает текущую установку указателя DTA (при возврате ES:BX содержат сегмент и смещение DTA).
Префикс программного сегмента PSP обеспечивает каждую программу 128-байтным встроенным DTA, начиная со смещения 80H и до 9FH. Программа может использовать его при нехватке памяти. Первоначально указатель на DTA указывает именно на этот буфер, поэтому если программа будет использовать его, то нет нужды устанавливать указатель. Этот буфер по умолчанию особенно удобно использовать с COM файлами, где DS указывает на начало префикса программного сегмента. Для файлов EXE может потребоваться небольшой добавочный код, чтобы использовать DTA по умолчанию.

Примечание: после извлечения года к нему необходимо прибавить 1980.
Существует ряд функций для работы с файлами, используя DTA. Наиболее употребимые из них приведены ниже.
Функция 1ah
Установить адрес DTA.
Вход. AH=1aH
DS:DX=адрес для DTA
Выход. Нет
Описание. Устанавливает адрес DTA.
Функция 2fh
Дать текущий DTA.
Вход. AH=2fH
Выход. ES:BX=адрес для DTA
Описание. Возвращает адрес начала области ввода-вывода (DTA). Поскольку DTA глобальна для всех процессов, в рекурсивной процедуре (например, при проходе по дереву оглавления) может потребоваться со-хранить адрес DTA, а впоследствии восстановить его посредством функ-ции 1aH.
Функция 4eh
Найти 1-й совпадающий файл.
Вход. AH=4eH
DS:DX=адрес строки ASCIIZ с именем файла
CX=атрибут файла для сравнения
Выход. AX=код ошибки, если CF установлен
DTA=заполнена данными (если не было ошибки)
Описание. DS:DX указывает на строку ASCIIZ в форме: «d:\путь\имяфайла»,0. Если диск и/или путь опущены, они подразумевают-ся по умолчанию. Обобщенные символы * и ? допускаются в имени файла и расширении. DOS находит имя первого файла в оглавлении, которое совпадает с заданным именем и атрибутом, и помещает найденное имя и другую информацию в DTA.
Функция 4fh
Найти следующий совпадающий файл.
Вход. AH=4fH
DS:DX= адрес данных, возвращенных предыдущей 4eH.
Выход. AX=код ошибки, если CF установлен
DTA=заполнена данными (если не было ошибки)
Описание. DS:DX указывает на 2bH-байтовый буфер с информаци-ей, возвращенной функцией 4eH (либо DTA, либо буфер, скопированный из DTA).Необходимо использовать эту функцию после вызова 4eH. Сле-дующее имя файла, совпадающее по обобщенному имени и атрибуту фай-ла, копируется в буфер по адресу DS:DX вместе с другой информацией (см. функцию 4eH о структуре файловой информации в буфере, заполняе-мом DOS).
Ниже приведен фрагмент программы, иллюстрирующий организацию поиска файлов в текущем каталоге.

;Установить адрес DTA
MOV AH,01AH
LEA DX,FDTA
INT 21H
. . .
;Наити первый совпадающий файл
MOV AH,4EH
LEA DX,MASKA
MOV CX,10H
INT 21H
JC EXIT
NEXT:
. . .
;Найти следующий совпадающий файл
MOV AH,4FH
MOV CX,10H
LEA DX,MASKA
INT 21H
JNC NEXT
EXIT:
. . .
;========== DTA =========
FDTA DB 15H DUP (?)
FATTR DB ?
FTIME DW ?
FDATA DW ?
FSIZE DD ?
FNAME DB 0DH DUP (‘$’)
;========================
MASKA DB ‘*.*’,0

Структура PSP

Префикс программного сегмента PSP (Program Segment Prefics) – специальная область оперативной памяти размером 256 (100h) байт. PSP может использоваться в программе для определения имен файлов и пара-метров из командной строки, введенной при запуске программы на выпол-нение, объема доступной памяти, переменных окружения системы и так далее. После загрузки программы в память сегментные регистры DS и ES указывают на начало PSP этой программы.

Окружение DOS

DOS поддерживает область памяти, содержащую набор строк ASCIIZ, которые могут использоваться приложениями для получения некоторой системной информации и для передачи данных между программами. Эта область памяти называется окружением DOS.
Структура окружения DOS:

имя_1=значение_1«0»
имя_2=значение_2«0»
. . .
имя_N=значение_N«0»
«0»
«xxxx»
EXEC_string_1«0»
. . .
EXEC_string_NN«0»
«0»

Здесь «0» — это символ ASCII NUL (00H), а «xxxx» — 16-битовое дво-ичное значение (количество дополнительных строк).
Окружение не превышает 32K байт и начинается на границе парагра-фа. Смещение 2cH в PSP текущей программы содержит номер параграфа окружения. Используя окружение, можно найти нужное имя’ серией срав-нений строк ASCIIZ, пока не достигнута пустая строка (нулевой длины), что указывает конец окружения.
В последних версиях DOS, за концом официального окружения по-мещается дополнительная строка, которая содержит диск и путь, с которых была загружена программа. Вслед за последней строкой ASCIIZ окруже-ния находится нулевой байт, указывающий конец официального окруже-ния. Следующие два байта содержат 16-битовый двоичный счетчик допол-нительных строк (обычно 0001H). Вслед за значением счетчика находится строка ASCIIZ, содержащая путь и имя файла. Это в точности та строка, которая использовалась функцией DOS 4bH (EXEC) для загрузки и запуска программы.

Работа с прерываниями

Иногда необходимо выполнить одну из набора специальных проце-дур, если в системе или в программе возникают определенные условия, например, нажата клавиша на клавиатуре. Действие, стимулирующее вы-полнение одной из таких процедур, называется прерыванием. Существует два общих класса прерываний: внутренние и внешние. Первые иницииру-ются состоянием ЦП или командой, а вторые — сигналом, подаваемым от других компонентов системы.
Переход к процедуре прерывания осуществляется из любой програм-мы, а после выполнения процедуры прерывания обязательно происходит возврат в прерванную программу. Перед обращением к процедуре преры-вания должно быть сохранено состояние всех регистров и флагов, исполь-зуемых процедурой прерывания, а после окончания прерывания эти реги-стры должны быть восстановлены.
Последовательность прерывания состоит в следующем:
1) текущее значение регистра Flags включается в стек;
2) текущее значение регистра CS включается в стек;
3) текущее значение регистра IP включается в стек;
4) сбрасываются флаги IF и TF.
Новое содержимое IP и CS определяет начальный адрес выполняемой процедуры прерывания (обслуживание прерывания). Возврат в прерван-ную программу осуществляется командой, которая извлекает из стека со-держимое для IP, CS и регистра флагов (обычно это команда IRET).
Адреса подпрограмм обслуживания прерываний (вектора прерыва-ний) хранятся в таблице векторов прерываний. Таблица векторов прерыва-ний располагается по адресу 0000:0000 и представляет собой массив из 256 элементов, каждый элемент которого занимает 4 байта и представ-ляет собой начальный адрес процедуры обработки прерывания.
Иногда в программе возникает необходимость переопределения (пе-рехвата) прерываний (например, выполнение дополнительных действий при нажатии определенной клавиши клавиатуры). Процесс перехвата пре-рываний состоит в следующем:
1) подготавливается FAR-процедура – новый обработчик прерыва-ний (должна заканчиваться командой IRET);
2) сохраняется старый вектор прерывания (функция 35h прерывания 21h)
3) адрес нового обработчика заносится в таблицу векторов прерыва-ний (функция 25h прерывания 21h);
4) в конце программы происходит восстановление первоначального обработчика прерываний.
Функция 35h
Вход. AH=35H
AL=номер прерывания (00H до 0ffH)
Выход. ES:BX=адрес обработчика прерывания
Описание. Возвращает значение вектора прерывания для INT (AL),
то есть загружает в BX 0000:[AL*4], а в ES –
0000:[(AL*4)+2].
Функция 25h
Вход. AH=25H
AL=номер прерывания (00H до 0ffH)
DS:DX=вектор прерывания (адрес подпрограммы)
Выход. Нет
Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL равным DS:DX. Это равно-сильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, в момент записи прерывания будут заблокированы.

Разбор практической программы

Задание: Создать текстовый файл «Dir.Txt», содержащий перечень файлов в текущем каталоге. Написать программу переопределения прерывания 05h (клавиша Print Screen).
Примечание:
1) файл «Dir.txt» можно создавать через описатели;
2) получение пути по которому была запущена программа (первая строка в файле «Dir.Txt») можно осуществить, используя окружение DOS. Для этого, во-первых, необходимо получить адрес PSP (функция 062H прерывания 21H), во-вторых, найти в нем адрес окружения DOS. В-третьих, получив из окружения DOS строку, содержащую путь и имя за-пущенного файла, выделить из нее путь к текущему каталогу;
3) на следующем этапе производится поиск первого совпадающего с маской «*.*» файла и его имя записывается в файл «Dir.Txt». Перед на-чалом поиска необходимо правильно установить атрибут файла в CX для сравнения. Далее производится поиск следующего совпадающего с маской файла, используя функцию 04FH прерывания 21H. Если такой файл най-ден, то его имя записывается в «Dir.Txt», иначе осуществляется выход из программы;
4) перед загрузкой нового вектора прерывания необходимо сохранить старый вектор (функция 35h прерывания 21h);
5) новый обработчик прерывания должен быть FAR-процедурой;
6) для проверки, новая процедура обработки прерывания 05h должна выводить в динамик сигнал (прерывание 21H). В основной программе не-обходимо организовать большой цикл, например, выводящий на экран символы (прерывание 21h использовать нельзя, можно использовать, на-пример, прерывание 10h). Таким образом, при нажатии на PrintScreen во время этого цикла компьютер должен издавать сигнал.

Файл fandp.asm
.model tiny
.186
.code
;благодаря этой директиве мы сразу имеем адрес окружения! :) удобно! :)
org 2Ch
okr dw ?
org 100h
start:
;так можно загрузить сегм. регистр! :)
push okr
pop es
;начали искать строку с именем в окружении DOS
xor ax,ax
xor di,di
cld
met:scasb
jne d
cmp es:[di+1],byte ptr 0
je quit
d:jmp met
quit:add di,2
xor si,si
ms:mov al,byte ptr es:[di]
cmp al,0
je poka
mov path[si],byte ptr al
inc si
inc di
jmp ms
;закончили сканировать окружение в поисках пути
;перехватываем прерывание
poka:sub si,9
mov ax,3505h
int 21h
mov word ptr oldint, bx
mov word ptr oldint+2,es
mov ax,2505h
mov dx,offset inter
int 21h
;выдаем писк! :) а то принт скрин всё равно нажать не успеем! :)
int 5h
mov ah,0h
int 16h
lea dx,mesg
mov ah,9
int 21h
;открываем файл
call openfile
mov handle,ax
mov ah,40h
mov bx,handle
mov cx,si
lea dx,path
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h
;установить адрес dta
mov ah,01ah
lea dx,fdta
int 21h
;начать поиск файлов
mov ah,4eh
lea dx,maska
mov cx,10h
int 21h
jc exit
mov di,0
mov cx,14
m0:cmp fname[di],’0′
je h0
inc di
h0:loop m0
mov ah,40h
mov bx,handle
mov cx,di
lea dx,fname
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h
next:mov ah,4fh
mov cx,10h
lea dx,maska
int 21h
jc exit
mov di,0
mov cx,14
m2:cmp fname[di],’0′
je h1
inc di
h1:loop m2
mov ah,40h
mov bx,handle
mov cx,di
lea dx,fname
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h
xor ax,ax
mov di,0
mov cx,14
m1:mov fname[di],al
inc di
loop m1
jmp next
exit: mov ah,3eh
mov bx,handle
int 21h
;восстанавливаем вектор обратно
mov ax,2505h
mov dx,word ptr oldint+2
mov ds,dx
mov dx,word ptr cs:oldint
int 21h
ret
;процедура обработки прерывания
inter proc far
pusha
push es
push ds
mov dl,07
mov ah,02
int 21h
pop ds
pop es
popa
jmp cs:oldint
inter endp
;процедура открытия файла
openfile proc near
mov ah,3ch
mov cx,0
mov dx,offset buf
int 21h
jnc nerr
mov dx,offset myerr
mov ah,9
int 21h
;перевод строки
mov dx,offset crlf
mov ah,09h
int 21h
nerr: ret
openfile endp
;.
oldint dd ?
mesg db «Find files$»
crlf db 0Dh,0Ah,’$’
buf db «filedir»,0
myerr db «WARNING.
File not create$»
handle dw ?
maska db «*.*»,0
fdta db 15h dup (?)
fattr db ?
ftime dw ?
fdata dw ?
fsize dd ?
fname db 14 dup (‘0’)
path db 256 dup (?)
end start

Компиляция :
c:\specprog\tasm\bin\tasm.exe /m fandp.asm
c:\specprog\tasm\bin\tlink.exe fandp.obj /t/x

Вот что имеем в результате запуска:
(под эмулятором и на реальном DOS при запуске вы услышите «писк»)
список найденых файлов сохраняется в файле filedir

Приложение Б Функции DOS (INT 21h)

Приложение Б Функции DOS (INT 21h)

DOS, функция 00h

CS – сегмент PSP завершающегося процесса

Описание. Передает управление на вектор завершения в PSP (выходит в родительский процесс). Идентична функции INT 20h (Terminate). Регистр CS должен указывать на PSP. Восстанавливает векторы прерываний DOS 22h-24h (Завершение, Ctrl-Break и Критическая ошибка), устанавливая значения, сохраненные в родительском PSP. Выполняет сброс файловых буферов. Файлы должны быть предварительно закрыты, если их длина изменилась.

Данная функция не рекомендуется к использованию. Для выхода из программы лучше использовать функцию DOS 4Ch.

DOS, функция 01h Считать со стандартного устройства ввода

Выход: AL – символ, полученный из стандартного ввода

Описание. Считывает (ожидает) символ со стандартного входного устройства. Отображает этот символ на стандартное выходное устройство (эхо). При обнаружении Ctrl-Break выполняется INT 23h.

Ввод расширенных клавиш ASCII (F1-F12, PgUp, курсор и другие) требует двух обращений к этой функции. Первый вызов возвращает AL=0. Второй вызов возвращает в AL расширенный код ASCII.

DOS, функция 02h Записать в стандартное устройство вывода

DL – символ, выводимый в стандартный вывод

Посылает символ из DL в стандартное устройство вывода. Обрабатывает символ Backspace (ASCII 8), перемещая курсор влево на одну позицию и оставляя его в новой позиции. При обнаружении Ctrl-Break выполняется INT 23h.

DOS, функция 03h Считать символа со стандартного вспомогательного устройства

Выход: AL – символ, введенный со стандартного вспомогательного устройства

Описание. Считывает (ожидает) символ со стандартного вспомогательного устройства, COM1 или AUX и возвращает этот символ в AL.

Ввод не буферизуется и должен опрашиваться (не управляется прерываниями). При запуске DOS порт AUX (COM1) инициализируется так: 2400 бод, без проверки на четность, 1 стоп-бит, 8-битные слова. Команда DOS MODE используется для установки иных характеристик.

DOS, функция 04h Записать символ в стандартное вспомогательное устройство

DL – символ, выводимый в стандартное вспомогательное устройство

Посылает символ, находящийся в регистре DL, на стандартное вспомогательное устройство, COM1 или AUX.

DOS, функция 05h Вывести на принтер

DL – символ, записываемый на стандартный принтер

Посылает символ в DL на стандартное устройство печати, обычно LPT1.

DOS, функция 06h Консольный ввод-вывод

DL=00h-FEh – символ, посылаемый на стандартный вывод

DL=FFh – запрос ввода со стандартного ввода

ZF=0, если осуществлялся ввод символа и символ готов при запросе ввода

AL – считанный символ

ZF=1, если осуществлялся ввод символа и символа в консоли нет

При DL=0FFh выполняет ввод с консоли «Без ожидания», возвращая включенный флаг нуля ZF, если на консоли нет готового символа. Если символ готов, сбрасывает флаг ZF и возвращает считанный символ в AL. Если DL не равен 0FFh, то DL направляется на стандартный вывод.

DOS, функция 07h Нефильтрующий консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. Не проверяет на Ctrl-Break, BackSpace и другие.

Для ввода расширенного символа ASCII должна быть вызвана дважды. Для проверки статуса используется функция DOS 0Bh (чтобы не ожидать нажатия клавиши).

DOS, функция 08h Консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. При обнаружении Ctrl-Break выполняется прерывание INT 23h.

Для ввода расширенного символа ASCII должна быть вызвана дважды.

DOS, функция 09h Запись строки на стандартный вывод

DS:DX – адрес строки, заканчивающейся символом «$» (ASCII 24h)

Строка, исключая завершающий ее символ «$», посылается на стандартный вывод. Символы Backspace обрабатываются как в функции 02h (вывод на дисплей). Чтобы перейти на новую строку, обычно включают в текст пару CR/LF (ASCII 0Dh и ASCII 0Ah). Строки, содержащие «$», можно передать на стандартное устройство вывода с помощью функции 40h (BX=0).

DOS, функция 0Ah Ввод строки в буфер

DS:DX – адрес входного буфера (Таблица Б-1)

Таблица Б-1. Формат входного буфера

Буфер содержит введенные данные, в конце – символ CR (ASCII 0Dh)

DOS, функция 0Bh Проверка статуса ввода

Выход: AL=FFh, если символ доступен со стандартного ввода AL=00h, если нет доступного символа

Описание. Проверяет состояние стандартного ввода. При распознавании Ctrl-Break выполняется INT 23h.

Используется перед функциями 01h, 07h и 08h, чтобы избежать ожидания нажатия клавиши.

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

DOS, функция 0Ch Ввод с очисткой

AL – номер функции ввода DOS:

AL=01h – ввод с клавиатуры

AL=06h – ввод с консоли

AL=07h – нефильтрующий без эха

AL=08h – ввод без эха

AL=0Ah – буферизованный ввод

Очищает буфер опережающего ввода стандартного ввода, а затем вызывает функцию ввода, указанную в AL. Это заставляет систему ожидать ввод очередного символа.

DOS, функция 0Dh Сброс диска

Сбрасывает диск (записывает на диск все файловые буферы). Файл, размер которого изменился, должен быть предварительно закрыт (при помощи функций 10h или 3Eh).

DOS, функция 0Eh Установить текущий диск DOS

DL – номер диска (0 – A, 1 – B и так далее), который становится текущим

Выход: AL – общее число дисководов в системе

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

AL имеет то же значение, что и LASTDRIVE, указанное в файле CONFIG.SYS, и по умолчанию равно 5.

DOS, функция 0Fh Открыть файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

Таблица Б-2. Формат FCB

AL=00h, если функция выполнена успешно (FCB заполнен)

AL=FFh, если файл не найден или доступ к файлу не разрешен

Файл, описываемый неоткрытым FCB, должен существовать в текущем оглавлении на диске, специфицированном в FCB (0 – текущий, 1 – A, 2 – B и так далее). Если файл не существует, возвращается AL=0FFh. Файл открывается в режиме совместимости. Если поле «Номер диска» в FCB равно нулю в момент вызова, то оно заполняется номером текущего дисковода (1 – A, 2 – B и так далее). Поле FCB «Номер текущего блока» устанавливается в ноль. Поле FCB «Размер логической записи» устанавливается в 80h. Поля даты и размера файла в FCB устанавливаются из оглавления.

DOS, функция 10h Закрыть файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден там, где он находился при открытии с помощью функции 0Fh

Закрывает файл, открытый функцией 0Fh. Файл должен находиться на своем первоначальном месте в текущем оглавлении диска, на котором он был открыт. Если файл найден, оглавление обновляется, файловые буфера сбрасываются и возвращается AL=00h. Если файл не найден, оглавление не обновляется и возвращается AL=FFh.

DOS, функция 11h Найти первый совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

AL=FFh, если подходящего имени нет

В текущем оглавлении DOS происходит поиск файлов с именем, соответствующим заданному шаблону. При неудаче возвращается AL=0FFh. Если имя найдено, AL очищается, в первый байт DTA помещается номер дисковода (A – 1, B – 2 и так далее), а в следующие 32 байта помещается элемент оглавления для найденного файла.

Можно использовать при вызове расширенный FCB, чтобы выбирать файлы с указанными атрибутами. В этом случае в DTA помещаются: байт FFh, 7 байт нулей, номер диска и элемент оглавления.

DOS, функция 12h Найти следующий совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

DTA заполнен AL=FFh, если подходящего имени нет

Используется после вызова функции 11h (Найти первый совпадающий файл через FCB) с обобщенным именем файла. Каждый последующий вызов заполняет DTA очередным подходящим элементом оглавления и возвращает AL=00h. Если подходящих имен больше нет, возвращается AL=FFh.

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

DOS, функция 13h Удалить файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден или доступ к файлу не разрешен

Эта функция удаляет все подходящие файлы в текущем оглавлении указанного диска согласно спецификации в FCB. Если подходящие файлы не найдены или если доступ отвергнут (как при попытке удалить файл с атрибутом Read-Only), функция возвращает в регистре AL значение FFh.

DOS, функция 14h Последовательное чтение из файла через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA содержит данные

AL=01h, если достигнут конец файла (EOF) и данные не считаны

AL=02h, если произошел выход за сегмент (чтения не было)

AL=03h, если EOF и считана усеченная запись (дополнена нулями)

Функция читает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать CurRec в ноль, так как функция 0Fh не инициализирует это поле.

DOS, функция 15h Последовательная запись в файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, если ошибка переполнения диска (данные не записаны)

AL=02h, если произошел выход за сегмент (записи не было)

Функция записывает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать «Номер текщей записи» в ноль, так как функция 0Fh не инициализирует это поле.

DOS буферизует данные, записывая полный сектор за один раз.


DOS, функция 16h Создание файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно FCB заполнен

AL=FFh, если при выполнении функции возникли ошибки

Описание. Файл, специфицированный неоткрытым FCB, создается на диске, указанном в FCB (0 – текущий, 1 – A и так далее). Он открывается в текущем оглавлении этого диска. FCB заполняется аналогично функции 0Fh. Если файл существует в момент вызова, его элемент оглавления перекрывается новым файлом, а длина файла сбрасывается в ноль.

Handle-ориентированные функции DOS 2.0+ гораздо удобнее в работе.

DOS, функция 17h Переименовать файл через FCB

DS:DX – адрес измененного FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Переименовывает файл в текущем оглавлении.

DOS, функция 19h Получить текущий диск DOS

Выход: AL – номер текущего диска (0 – A, 1 – B, и так далее)

Возвращает номер дисковода текущего диска DOS.

DOS, функция 1Ah Установить адрес DTA

DS:DX – адрес DTA

Устанавливает адрес DTA. Все FCB-ориентированные операции работают с DTA. DOS не позволяет операциям ввода/вывода пересекать границу сегмента. Функции поиска 11h, 12h, 4Eh и 4Fh помещают данные в DTA. DTA глобальна, поэтому надо проявлять осторожность при назначении ее в рекурсивной процедуре. При запуске программы ее DTA устанавливается по смещению 80h относительно PSP.

DOS, функция 1Bh Получить информацию FAT для текущего диска

DS:BX – адрес байта FAT ID, отражающего тип диска (Таблица Б-3)

DX – всего кластеров (единиц распределения) на диске

AL – секторов на кластер

CX – байт на сектор

Таблица Б-3. Значения >

Возвращает информацию о размере и типе текущего диска. Размер диска (в байтах) равен DX*AL*CX. Свободную память можно найти функциями 36h или 32h.

Версии: DOS 1.x держит FAT в памяти и возвращает DS:BX => FAT. DOS 2.0+ может держать в памяти лишь часть всей FAT.

Эта функция изменяет содержимое регистра DS.

DOS, функция 1Ch Получить информацию FAT для указанного диска

DL – номер диска (0 – текущий, 1 – A и так далее)

DS:BX – адрес байта FAT ID, отражающего тип диска (приведен в описании функции 1Bh)

DX – всего кластеров (единиц распределения)

AL – секторов на кластер

CX – байт на сектор

Аналогична функции 1Bh с той разницей, что регистр DL указывает диск, для которого нужно получить информацию.

DOS, функция 21h Считать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA заполнена данными

AL=01h, если достигнут конец файла (EOF) и чтения не было

AL=02h, если произошел выход за сегмент (чтения нет)

AL=03h, если встречен EOF и усеченная запись дополнена нулями

Данная функция читает из файла с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 22h Писать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, при переполнении диска

AL=02h, если DTA+FCB выходит за сегмент (нет записи)

Данная функция записывает в файл с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 23h Получить размер файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Проще определить размер файла при помощи функции 3Dh с последующим выполнением 42h (при AL=2).

DOS, функция 24h Установить адрес произвольной записи в файле

DS:DX – адрес открытого FCB (Таблица Б-2)

Устанавливает поле «Номер записи при непосредственном доступе к файлу» в FCB на файловый адрес, соответствующий значениям полей «Текущий блок» и «Запись с текущей позиции».

DOS, функция 25h Установить вектор прерывания

AL – номер прерывания

DS:DX – вектор прерывания – адрес программы обработки прерывания

Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL, равным DS:DX. Это равносильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, DOS знает, что происходит, и гарантирует, что в момент записи прерывания будут заблокированы.

Восстановить DS (если необходимо) после этого вызова.

DOS, функция 26h Создать новый PSP

DX – адрес сегмента (параграфа) для нового PSP

CS – сегмент PSP, используемый как шаблон для нового PSP (Таблица Б-4)

Описание. Устанавливает PSP для порождаемого процесса по адресу DX:0000. Текущий PSP (100h байт, начиная с CS:0) копируется в DX:0000h, поле MemTop соответственно корректируется, векторы Terminate, Ctrl-Break и Critical Error копируются в PSP из векторов прерываний INT 22h, INT 23h и INT 24h. После этого можно загрузить программу с диска и передать ей управление посредством FAR JMP.

Если перехватывается INT 21h, нужно позаботиться о помещении в стек корректного CS: IP. Еще лучше использовать функцию 4Ch.

Таблица Б-4. Формат PSP

DOS, функция 27h Читать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число считываемых записей

Выход: AL=00h, если чтение успешно и DTA заполнена данными AL=01h если достигнут конец файла (EOF) и данные не считаны AL=02h, если при чтении произошел выход за границу сегмента AL=03h, если EOF и считана усеченная порция (дополнена нулями) CX – действительное число считанных записей

Читает несколько записей из файла, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Помещает данные в память, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 28h Писать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число записываемых блоков (если CX равен нулю, то размер файла усекается до указанного в поле FCB «Номер записи при непосредственном доступе к файлу»)

AL=00h, если запись успешна

AL=01h, при переполнении диска

AL=02h, если при записи произошел выход за границу сегмента

CX – действительное число сделанных записей

Описание. Записывает несколько блоков в файл, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Читает данные из памяти, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 29h Разобрать имя файла

DS:SI – адрес исходной текстовой строки для разбора

ES:DI – адрес буфера для результирующего неоткрытого FCB (Таблица Б-2)

AL – битовые флаги, указывающие опции разбора (Таблица Б-5).

AL=00h, если результирующий FCB не содержит обобщенных символов

AL=01h, если результирующий FCB содержит обобщенные символы

AL=FFh, если неверно обозначение диска в имени файла

DS:SI – изменен – указывает на символ сразу вслед за именем файла

ES:DI – не изменен – указывает на неоткрытый FCB

Создает неоткрытый FCB из строки текста или параметра команды. Текст, начиная с DS:SI, анализируется как имя файла в формате D: FILENAME.EXT, и буфер по адресу ES:DI заполняется как соответственно форматированный FCB.

Таблица Б-5. Битовые флаги

DOS, функция 2Ah Получить системную дату

AL – день недели (0 – воскресенье, 1 – понедельник, … 6 – суббота), DOS 3.0+

CX – год (от 1980 до 2099)

DH – месяц (1 до 12)

DL – день (1 до 31)

Описание. Возвращает текущую дату, которая известна системе.

DOS 2.x не гарантирует возврата в AL значения дня.

DOS 1.0+ возвращает правильный день недели.

Версии до 2.1 имеют проблемы с переходом через дату.

DOS, функция 2Bh Установить системную дату

CX – год (от 1980 до 2099)

DH – месяц (от 1 до 12)

DL – день (от 1 до 31)

AL=00h, если дата корректна

AL=FFh, если дата некорректна и не изменена

Устанавливает системную дату DOS.

DOS, функция 2Ch Получить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

Описание. Возвращает текущее время, которое известно системе.

Поскольку системные часы имеют частоту 18.2 Гц (интервал 55мс), DL имеет точность примерно 0.04 сек.

DOS, функция 2Dh Установить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

AL=00h, если время корректно

AL=FFh, если время некорректно и не изменено

Устанавливает системное время DOS.

DOS, функция 2Eh Установить/сбросить переключатель верификации

AL=00h – отключить верификацию

AL=01h – включить верификацию

Описание. Задает, должна ли DOS верифицировать (считывать обратно) каждый сектор, записываемый на диск. Это замедляет операции записи на диск, но гарантирует максимальную надежность записи.

DOS, функция 2Fh Получить адрес текущей DTA

Выход: ES:BX – адрес начала текущей DTA

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

Примечание. Эта функция изменяет сегментный регистр ES.

Версии: DOS 2.00 и выше DOS, функция 30h Получить номер версии DOS

AL – старший номер версии

AH – младший номер версии

BL:CX – 24-битный серийный номер (большинство версий не поддерживают этот параметр)

Описание. Возвращает в AX значение текущего номера версии DOS. Например, для DOS 3.20 в AL возвращается 03h, в AH – 14h.

Примечание. Если в AL возвращается 00h, можно предполагать, что работает DOS более ранней версии, чем DOS 2.0.

Версии: DOS 2.00 и выше. DOS, функция 31h Завершиться и остаться резидентным

DX – объем памяти, оставляемой резидентной (в параграфах)

Описание. Выходит в родительский процесс, сохраняя код выхода в AL. Код выхода можно получить через функцию 4Dh. DOS устанавливает начальное распределение памяти, как специфицировано в DX, и возвращает управление родительскому процессу, оставляя указанную память резидентной (число байт равно DX*16). Эта функция перекрывает функцию INT 27h, которая не возвращает код выхода и не способна установить резидентную программу, размер которой превышает 64 Кбайт.

Diplom Consult.ru

Лабораторная работа № 1.

Обработка прерываний

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

Обработка прерывания

Рассмотрим подробно действия, которые называются обработкой прерывания. Эти действия выполняются независимо от того вызвано ли прерывание аппаратно или программно. При получении сигнала на прерывание (при аппаратном прерывании от программируемого контроллера прерываний, при программном командой процессора int) процессор содержимоеCS,IPи регистра флагов сохраняет в стеке. ВCSиIPпомещается адрес подпрограммы обработки прерывания, которая и выполняется, после чего восстанавливаются из стека содержимое CS, IP и регистра флагов, и процессор продолжает выполнение программы.

Двойное слово, в котором хранится адрес подпрограммы обработки прерывания, называется вектором прерывания. Всего допустимо иметь 256 различных векторов прерываний. Для хранения векторов прерываний в DOSвыделен первый килобайт памяти. Адрес вектора прерывания с номеромNвычисляется какN*4. В младшем слове хранится значениеIP, а в старшемCS.

Пример. Определить адрес прерывания 21h. Получим 21h* 4h=84h. Просмотр содержимого четырёх байтов, начиная с 0084hв сегменте 0000h

Показывает, что там храниться число

где 0DDE– адрес сегмент, а 048B– адрес смещения подпрограммы обработки прерывания с номером 21h.

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

Каждая подпрограмма обработки прерывания, в отличие от обычно подпрограммы, завершается командой iret, которая похожа на командуret, но восстанавливает из стека кромеCSиIPещё и регистр флагов.

Изменение вектора прерывания

MS-DOSпредоставляет две функции 35hи 25hпрерывания 21hдля чтения и установки вектора прерывания.

Функция 35h

Выполняет чтение адреса подпрограммы обработки прерывания.

al= номер прерывания

es:bx– указатель на подпрограмму прерывания

Примечание. Функция получает адрес, указанного ALпрерывания из таблицы векторов прерываний.BXсодержит смещение, аESсегмент адреса подпрограммы.

movah, 35h; номер функции

moval, 21h; номер прерывания

В результате: (BX) = 048B, (ES) = 0DDE.

Выполняет занесение нового вектора прерывания.

al= номер прерывания

ds:dx– указатель на подпрограмму обработки прерывания

Примечание. Функция устанавливает адрес прерывания, указанного в alв таблицу векторов прерываний.dxсодержит смещение, аdsсегмент устанавливаемой подпрограммы.

Dos fn 35h: дать вектор прерывания

Программы обработки прерываний (или попросту обработчики прерываний) относятся к важнейшим программным средствам персональных компьютеров. Запросы на обработку прерываний могут иметь различную природу. Прежде всего, различают аппаратные прерывания от периферийных устройств или других компонентов системы и программные прерывания, вызываемые командой int, которая используется, в частности, для программного обращения к функциям DOS и BIOS. Сигналы, возбуждающие аппаратные прерывания, могут инициироваться цепями самого процессора, например, при попытке выполнения операции деления на ноль (такие прерывания называются внутренними, или отказами), а могут приходить из периферийного оборудования (внешние прерывания). Внешние аппаратные прерывания вызываются, например, сигналами микросхемы таймера, сигналами от принтера или контроллера диска, нажатием или отпусканием клавиши. Таким образом, можно говорить о прерываниях трех типов: внутренних, внешних и программных. Независимо от источника, действия процессора по обслуживанию поступившего прерывания всегда выполняются одинаково, как для аппаратных, так и для программных прерываний. Эти действия обычно называют процедурой прерывания. Подчеркнем, что здесь идет речь лишь о реакции самого процессора на сигнал прерывания, а не об алгоритмах обработки прерывания, предусматриваемых пользователем в обработчике прерываний.
Объекты вычислительной системы, принимающие участие в процедуре прерывания, и их взаимодействие показаны на рис. 25.1.
Самое начало оперативной памяти от адреса 0000h до 03FFh отводится под векторы прерываний — четырехбайтовые области, в которых хранятся адреса программ обработки прерываний (ПОП). В два старших байта каждого вектора записывается сегментный адрес ПОП, в два младших — относительный адрес точки входа в ПОП в сегменте. Векторы, как и соответствующие им прерывания, имеют номера, причем вектор с номером 0 располагается, начиная с адреса 0, вектор 1 — с адреса 4, вектор 2 — с адреса 8 и т.д. Вектор с номером N занимает, таким образом, байты памяти от N*4 до N*4+3. Всего в выделенной под векторы области памяти помещается 256 векторов.

рис. 4.1. Процедура прерывания

Получив сигнал на выполнение процедуры прерывания с определенным номером, процессор сохраняет в стеке выполняемой программы текущее содержимое трех регистров процессора: регистра флагов, CS и IP. Два последних числа образуют полный адрес возврата в прерванную программу. Далее процессор загружает CS и IP из соответствующего вектора прерываний, осуществляя тем самым переход на ПОП.
Программа обработки прерывания обычно заканчивается командой возврата из прерывания iret (interrupt return, возврат из прерывания), выполняющей обратные действия — загрузку IP, CS и регистра флагов из стека, что приводит к возврату в основную программу в ту самую точку, где она была прервана.
Большая часть векторов прерываний предназначена для выполнения определенных действий и автоматически заполняется адресами системных программ при загрузке системы;
часть векторов зарезервирована для будущих применений, а часть (конкретно с номерами 60h. 66h) свободна и может использоваться в прикладных программах.
Для того чтобы прикладной обработчик получал управление в результате прерывания, его адрес следует поместить в соответствующий вектор прерывания. Хотя содержимое вектора прерываний можно изменить простой командой mov, однако предпочтительнее использовать специально предусмотренную функцию DOS 25h. При вызове функции 25h в регистр AL помещается номер модифицируемого вектора, а в регистры DS:DX — полный двухсловный адрес нового обработчика.
Рассмотрим методику использования в прикладной программе прерывания пользователя.

Пример 4.1. Обработка прерываний пользователя

Процедура new_65h, вызываемая с помощью программного прерывания (для которого выбран вектор 65h), выполняет простую операцию — очищает экран, накладывая на него окно с заданным атрибутом.
В основной программе, прежде всего заполняется вектор прерывания 65h. Поскольку функция заполнения вектора 25h требует, чтобы адрес прикладного обработчика содержался в парс регистров DS:DX, a DS у нас указывает на сегмент данных, перед вызовом DOS в DS следует занести сегментный адрес того сегмента, в котором находится обработчик, т.е., в нашем случае, общего сегмента команд. Этот адрес извлекается из CS.
Далее в бесконечном цикле выполняется вызов нашего обработчика, позиционирование курсора с помощью функции 02h BIOS и вывод на чистый экран строки символов (функцией 0Ah BIOS). Эта функция не позволяет задавать атрибуты выводимых символов. Символы приобретают атрибут тех позиций, куда они выводятся, т.е., в нашем случае, атрибут окна. После вывода на экран строки выполняется изменение кода символов и номера строки экрана, куда эти символы выводятся.
Функция DOS 08h (ввод символа без эха), включенная в цикл, выполняет две задачи. Bo-первых, она останавливает выполнение программы и позволяет изучить содержимое экрана в каждом шаге цикла. Для того, чтобы продолжить выполнение программы, достаточно нажать на любую клавишу. Во-вторых, эта функция, будучи чувствительна к вводу с клавиатуры сочетания /C, позволяет завершить программу, которая в противном случае выполнялась бы вечно.

Обработчик прерываний от таймера.

Структура обработчика прерываний и его взаимодействие с остальными компонентами программного комплекса определяются рядом факторов, из которых важнейшими являются следующие:
• прерывания, инициализирующие обработчик, могут быть аппаратными (от периферийных устройств) или программными (команда int).
• обработчик может входить в состав прикладной программы или представлять собой самостоятельную единицу. В последнем случае он относится к специальному классу резидентных программ;
• вектор обрабатываемого прерывания может быть свободным и использоваться системой или какой-либо резидентной прикладной программой;
• если вектор уже используется системой, т.е. в составе DOS имеется системный или прикладной разработчик прерываний с данным номером, то новый обработчик может полностью заменять уже загруженный (превращая его тем самым фактически в бесполезную программу) или “сцепляться” с ним;
• в случае сцепления с загруженным ранее обработчиком новый обработчик может выполнять свои функции до уже имеющегося в системе или после него.
В настоящем разделе будут рассмотрены общие вопросы обработки прерываний на примере простого обработчика прерывания 1Ch.
Для того чтобы прикладные программы могли использовать сигналы таймера, не нарушая при этом работу системных часов, в программу BIOS, обслуживающую аппаратные прерывания от таймера, поступающие через вектор 08, включен вызов int 1Ch, передающий управление на программу-заглушку BIOS, которая содержит единственную команду iret (рис. 46.1.). пользователь может записать в вектор 1Ch адрес прикладного обработчика сигналов таймера и использовать в своей программе средства реального времени. Естественно, перед завершением программы следует восстановить старое значение вектора 1Ch.

Рис. 4.2 Прикладная обработка прерываний от таймера

При рассмотрении методики включения в программу процедур-подпрограмм мы отмечали, что порядок расположения процедур в программе не влияет на ход ее выполнения. Важно лишь так скомпоновать текст программы, чтобы подпрограммы никогда не активизировались “сами по себе”, иначе, чем в результате выполнения команды call в вызывающей программе. Это правило относится и к обработчикам прерываний, включаемым в состав программы. Текст обработчика можно расположить в любом месте программы, обеспечив лишь невозможность случайного перехода на его строки не в результате прерывания, а по ходу выполнения основной программы. Обычно обработчики располагаются либо в начале, либо в конце текста программы. В примере 46.1 текст обработчика идет вслед за текстом основной программы.
Другое замечание относится к оформлению программы обработчика. Так же, как и в случае подпрограмм, обработчик прерывания может образовывать процедуру (что наглядно), но может начинаться просто с метки. Важно лишь, чтобы последней выполняемой командой обработчика была команда iret.

Пример 4.2. Обработчик прерываний от таймера.

В рассмотренном примере используются макрокоманды из файла mac.mac. Для того, чтобы сделать доступными эти макрокомнда в нашей программе, используем директиву include. Файл mac.mac имеет следующее содержание:

Главная процедура начинается, как обычно, с инициализации сегментного регистра DS. Перед тем, устанавливать собственный обработчик какого-либо прерывания, следует сохранить его исходный (системный) вектор, чтобы перед завершением программы вернуть систему в исходное состояние. Для получения содержимого вектора 1Ch используется функция DOS 35h, которая возвращает содержимое указанного вектора в регистрах ES:BX. Для сокращения объема исходного текста программы и номер функции, и номер требуемого вектора заносятся в регистры АН и AL одной командой (предложение 7). Исходный вектор сохраняется в двухсловной ячейке old_1ch, объявленной директивой dd (define double, определить двойное слово) в сегменте данных программы. Однако команды пересылки не могут работать с двойными словами, поэтому сохраняемый вектор засылается из регистров ES:BX в память пословно, сначала в младшую половину ячейки old_1ch (предложение 9), затем в старшую, естественно, равен old_lch+2 (предложение 10). Поскольку ячейка old_1ch объявлена с помощью директивы dd, для обращения к ее составляющим (словам) необходимо включать в команду описатели word ptr (word pointer, указатель на слово), которые как бы отменяют на время трансляции команды первоначальное описание ячейки.
Сохранив вектор, мы можем приступить к заполнению его адресом нашего обработчика. Для этого используется функция DOS 25h, которая, как уже отмечалось, требует указания в регистре AL номера заполняемого вектора, а в регистрах DS:DX полного адреса обработчика, который и будет записан в указанный нами вектор. Однако регистр DS настроен на сегмент данных программы. Кстати, если бы это было не так, мы не могли бы выполнить предложения 9 и 10, так как поля данных программы адресуются через регистр DS. Поэтому на время выполнения функции 25h нам придется изменить содержимое DS, настроив его на тот сегмент, в котором находится процедура обработчика, т.е. на сегмент команд. Это и выполняется в предложениях 13. 15. Содержимое DS сохраняется в стеке, а затем в него через стек заносится содержимое регистра CS, который, очевидно, указывает на сегмент команд. После возврата из DOS в программу исходное содержимое DS восстанавливается (предложение 17).
Начиная с этого момента, прерывания от таймера, приводящие к выполнению в системной программе BIOS команды int 1Ch, будут активизировать 18,2 раз в секунду программу нашего обработчика. При этом вся наша программа должна находиться в памяти и что-то делать, так как если она завершена, то она и обработчик уйдут из памяти. Для задержки программы в ней предусмотрен многократный, в цикле вывод на экран строки текста (предложения 18. 21).
Перед завершением программы необходимо с помощью той же функции 25h восстановить исходное содержимое вектора 1Ch. Для загрузки регистров DS:DX требуемым адресом в примере 46.1 используется удобная команда Ids (load pointer using DS, загрузка указателя с использованием DS). В качестве операндов для этой команды указывается один из регистров общего назначения и двухсловное поле памяти с искомым адресом. Следует иметь в виду, что после выполнения этой команды старое содержимое регистра DS теряется. В нашем примере оно больше не нужно, так как, выполнив восстановление вектора, программа завершается (предложение 25). Вообще же перед выполнением команды Ids исходное содержимое регистра DS следует сохранить в стеке.
Рассмотрим теперь программу обработчика прерывания от таймера. Программа начинается с сохранения в стеке регистров, которые будут использоваться в обработчике. Это чрезвычайно важное действие, так как переход на программу обработчика осуществляется по команде int1ch из системной программы обработки прерываний от таймера. При выполнении процедуры прерывания процессор настраивает должным образом только регистры CS и IP. Содержимое всех остальных регистров (в том числе сегментных) отражает состояние системной программы, и если оно будет изменено, то после возврата из нашего обработчика в вызвавшую его системную программу она перестанет функционировать. В нашем обработчике используются лишь регистры АХ и ES, которые и сохраняются в стеке (предложения 28-29).
Далее регистр ES настраивается на адрес видеобуфера (предложения 30-31), а в регистр АХ помещается код ASCII выводимого на экран символа вместе с его атрибутом (предложение 32). В последнем предложении используется важная возможность замены сегмента. Как уже отмечалось, при обращении к памяти по умолчанию используется сегментный регистр DS, т.е. предполагается, что адресуемая ячейка находится в том сегменте, на который в настоящий момент указывает DS, в нашем же случае в момент выполнения этой команды DS почти, наверное указывает на какой-то сегмент программы BIOS. Для адресации к нашим данным можно сохранить содержимое DS и настроить его на наши данные. Однако можно поступить проще, именно, ввести в команду префикс замены сегмента CS: и тем самым указать транслятору, чтобы он в данной команде использовал адресацию через регистр CS. Но и данные в этом случае следует разместить не в сегменте данных, к которому нет доступа, а в сегменте команд. У нас так и сделано. Ячейки sym1 и sym2, к которым обращается обработчик, расположены в конце процедуры new_1ch в пределах сегмента команд (предложения 39-40).
Вывод одного и того же символа в одно и то же место экрана приведет к тому, что мы не будем знать, работает ли наш обработчик. В нашем примере предусмотрена периодическая смена атрибута символа, что делает символ мерцающим. Для этого при каждом проходе программы обработчика ячейки sym1 и sym2 взаимно обмениваются своим содержимым. В результате на экран выводится то один, то другой код. Для обмена содержимого регистров или ячеек предусмотрена специальная команда xchg (exchange, обмен). Поскольку в микропроцессорах 80х86 запрещены команды с адресацией к памяти в обоих операндах, обмен приходится осуществлять через регистр АХ.
После восстановления сохраненных в стеке регистров работа обработчика завершается командой iret, которая передает управление назад в вызвавшую наш обработчик программу BIOS. Когда эта программа дойдет до своего завершения, она выполнит команду iret и управление вернется в нашу программу в ту (неопределенную) точку, в которой она была прервана сигналом таймера.
Кроме того при необходимости работать с реальным временем можно использовать различные функции – одна из них AH=00h. Формат её использования имеет следующий вид:
INT 1Ah АН = 00h — Считать значение счетчика времени.
Ввод: АН = 00h
Вывод: CX:DX=значение счетчика, AL=байт переполнения счетчика.
Приведём пример программы, использующей эту функцию. Программа выводит на экран прямоугольник, который меняет цвет с периодичностью 3 секунды, выход по F10. Задержка реализована в макрокоманде. Пример 4.3

Общий алгоритм работы макроопределения задержки следующий: результат работы функции 00h возвращается в регистрах СX:DX. В данной программе работаем по регистру DX – младшие значения времени. Значение в регистре DX изменяется 18.2 раза в секунду, поскольку именно с такой периодичностью таймер вызывает аппаратное переывание. Сохраняем в регистре bx значение на единицу больше, чем в dx, затем сравниваем эти значения(строка 20). Всё это происходит в цикле. Как только значение dx измениться(прошло 18.2 сек) происходит выход из цикла. Данный цикл заключён во внешний цикл zd(строка 9). И поскольку внешний цикл cikl(строка 11) выполняется 18.2 раза в сек., то если внешний выполнить 18 раз, то общая задержка примерно равна 1 сек. Параметром, задающим количество повторений внешнего цикла, является параметр макрокоманды time. Т.е. команда Dely 18 – задержка примерно в 1 сек. для данного макроопределения.

Группа дисковых функций MS-DOS

В эту группу входят прерывания, предназначенные для выполнения основных функций операционной системы, в том числе для выполнения операций с логическими дисками, файлами и каталогами [3, 10]. Дисковые функции DOS обладают достаточной полнотой и универсальностью для решения любых задач в реальном режиме DOS. Они могут применяться и в режиме линейной адресации памяти, но информацию в расширенную память приходится пересылать через промежуточный буфер в первом мегабайте адресного пространства процессора. Впрочем, дополнительные пересылки не особенно замедляют работу: поиск данных на диске и передача информации между диском и процессором занимает гораздо больше времени, чем копирование такого же объема данных с одного участка оперативной памяти в другой.’

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

• строка ASCIIZ — текстовая строка в ASCII-коде, которая завершается нулевым значением;

• дескриптор файла — уникальный номер, который операционная система присваивает создаваемому или открываемому файлу в качестве идентификатора (чтобы потом обращаться к файлу по этому номеру вплоть до его закрытия).

Классические функции для работы с дисками

К этой группе относятся функции, появившиеся в ранних версиях операционной системы MS-DOS и сохранившиеся с тех пор практически без изменений. Такие функции отличаются крайне примитивной обработкой ошибок:

• в случае успешного завершения операции флаг CF сбрасывается в 0;

• в случае ошибки флаг CF устанавливается в 1.

Для обращения к дисковым функциям DOS используется прерывание Int 21h.

Прерывание Int 21 h, функция OEh: сменить текущий логический диск

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

• в АН — значение OEh;

• в AL — код логического диска (0 — А:, 1 — В: и т. д.).

После завершения операции функция возвращает в регистре AL максимально возможный в данной системе номер логического дисковода (определяется параметром LASTDRIVE в файле CONFIG.SYS).

Прерывание Int 21 h, функция 19h: определить номер текущего дисковода

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

Перед вызовом прерывания требуется записать в регистр АН значение 19h.

После завершения операции функция возвращает в регистре AL код логического диска (0 — А:, 1 — В: и т. д.).

Прерывание Int 21 h, функция 1Ah: изменить адрес области обмена с диском

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

Перед вызовом прерывания требуется записать в регистры следующие значения:

• в АН — значение lAh;

• в DS: DX — указатель на новый адрес буфера обмена DTA. ПРИМЕЧАНИЕ

При запуске программы ее область DTA первоначально установлена по адресу PSP:0080h.

Прерывание Int 21 h, функция 2Fh: получить адрес области обмена с диском

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

Перед вызовом прерывания требуется записать в регистр АН значение 2Fh.

После завершения операции функция возвращает в ES: ВХ указатель на адрес буфера обмена DTA.

Прерывание Int 21 h, функция 36h: определить объем свободного места на диске

Функция определяет объем свободного места на заданном логическом диске.

Перед вызовом прерывания требуется записать в регистры следующие значения:

• в АН — значение 36h;

• в AL — код логического диска (0 — А:, 1 — В: и т. д.).

В случае ошибки в регистре АХ будет возвращен код OFFFFh (недопустимый код логического диска).

В случае успешного завершения операции функция возвращает:

• в АХ — число секторов в кластере;

• в ВХ — число свободных кластеров;

• в СХ — число байтов в секторе;

• в DX — полное число кластеров на диске.

Объем свободного пространства определяется произведением содержимого регистров АХ, ВХ и СХ, а полный объем диска в байтах — произведением АХ, СХ и DX.

Улучшенные функции для работы с дисками

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

Перечисленные ниже функции DOS имеют усовершенствованные средства контроля: в случае ошибки, кроме установки флага CF, выдают в регистре АХ код ошибки, по которому можно определить причину ее возникновения. Возможные значения кодов ошибок приведены в табл. 6.1. Однако следует учитывать, что содержимое регистра АХ в случае успешного завершения данных функций не сохраняется.

Таблица 6.1. Значения расширенных кодов ошибки

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL