Итак, в этом уроке мы попробуем "почувствовать" прерывания. Что такое
прерывания ?
Допустим, вы решаете идете по лесу и собираете грибы. Ваш мозг, вероятно, думает о том, где же спрятались эти белые, лисички и т.п. Вдруг вас кусает комар. Практически не задумываясь вы бьете рукой по месту укуса и думаете себе дальше о грибах.
Проанализируем ситуацию с точки зрения посторения программного кода. Иногда это называют странными словами "построить блок-схему", но мы ограничимся обывательским методом описания:
Основная процедура поиска:
program MushroomsSerach;
{...}
repeat
Широта:=ШиротаМоегоГорода+random(10/СколькоПиваВзялиССобой);
Долгота:=ДолготаМоегоГорода+random(10/СколькоПиваВзялиССобой);
Лес:=ВыбратьЛесДляПоиска(Широта,Долгота);
if Лес.Выбран = True then
begin
for Лес.Грибы:=1 to Лес.ВсегоГрибов do
begin
if СправочникСъедобныхГрибовНеЗабыли:=True then
{...}
end;
until ПокаНеСтемнело;
Процедура обработки события "укусил комар":
Procedure OnStingByInsect(ТочкаУкусаX,ТочкаУкусаY: word);
begin
НанестиУдарПоТелу(ТочкаУкусаX+random(10),ТочкаУкусаY+random(10));
end;
Таким образом, мы написали костяк основной процедуры поиска грибов
и вспомогательную подпрограмму реакции на укус комара.
Как же нам связать их в одну целую программу ? Ведь в любой момент
выполнения основной программы может быть нужно выполнить подпрограмму обработки кусания и безболезненно вернуться к выполнению основной.
Здесь нам на помощь приходят так назывемые "прерывания". Смысл в том,
что будет использован некий диспетчер, который будет по необходимости
вызывать ту или иную подпрограмму.
В нижеизложенном примере решается следующая задача: основная программа рисует квадрат в некотором месте с некоего начального размера до максимального, но специальная подпрограмма, получающая управление
по тикам таймера принудительно изменяет случайным образом месторасположение квадрата на экране и его цвет, тем самым заставляя рисовать квадрат основной код на новом месте:
uses crt; { we use keypressed and readkey function form this module }
const
MyRandVal: word = 1;
MyScreenCenter: word = 0;
MaxBarSize = 20;
MyScreenBarSize: word = 1;
MyScreenColor: byte = 14;
var
SaveOldHandler: pointer;
CurrCoordinate: word;
CurrColor: byte;
IndX,IndY: word;
begin
asm
{ Устанавливаем графический режим монитора, 320x200, 256 цветов }
mov ax,0013h
int 10h
{ Устанавливаем обработчик прерывания 1Ch - таймер }
{ Сначала сохраняем старый обработчик }
{ Обратились к DOS за адресом старого обработчика }
mov ax,351Ch
int 21h
{ Адрес старого обработчика вернулся в es:bx }
{ Запомним его }
mov word ptr SaveOldHandler,bx
mov word ptr SaveOldHandler+2,es
{ Зададим наш обработчик прерывания 1Ch }
push ds { Сохранить регистр ds }
mov dx,offset @@OurHandlerOf1ChVector
mov ax,cs
mov ds,ax
{ Обратились к DOS, чтобы он установил новый обработчик }
mov ax,251Ch
int 21h
pop ds { Восстановить регистр ds }
{ Обойдем обработчик прерывания и попадем в цикл ожидания клавиш }
jmp @@WaitForAnyKey
{ Наш обработчик прерывания 1Ch }
@@OurHandlerOf1ChVector:
{ В эту точку программы мы попадаем примерно 18 раз в секунду }
{ Выберем случайное число }
{ Но для обращения к ячекам памяти необходимо настроить сегментные регистры }
{ Например, для обращения к переменной MyRandVal }
{ Все используемые регистры необходимо сохранять ! }
push es { Сохранить регистр es }
push ax { Сохранить регистр ax }
push dx { Сохранить регистр dx }
mov ax,seg @Data { Так в языке Паскаль можно получить СЕГМЕНТНЫЙ адрес }
mov es,ax { области данных (var + const) }
{ Умножаем ячеку памяти MyRandVal на число 16807 }
mov ax,16807
mul word ptr es:MyRandVal
{ dx:ax = MyRandVal * 16807 }
{ Запоминаем значение (MyRandVal * 16807) mod 2^16 в ячейке MyRandVal }
mov word ptr es:MyRandVal,ax
{ Инициализируем новое значение центра на экране }
mov word ptr es:MyScreenCenter,ax
{ Инициализируем начальное значение размера квадрата на экране }
mov word ptr es:MyScreenBarSize,1
{ Инициализируем новое значение цвета на экране }
mov byte ptr es:MyScreenColor,al
pop dx { Восстановить регистр dx }
pop ax { Восстановить регистр ax }
pop es { Восстановить регистр es }
iret
@@WaitForAnyKey:
end;
{ Цикл опроса клавиатуры ~ repeat until keypressed }
repeat
{ Рисуем квадрат размером MyScreenBarSize с центром в MyScreenCenter }
CurrColor:=MyScreenColor;
for IndY:=1 to MyScreenBarSize do
begin
for IndX:=1 to MyScreenBarSize do
begin
CurrCoordinate:=MyScreenCenter+IndY*320+IndX;
asm
{ Заносим в регистр es адрес видеопамяти - 0A000h }
mov ax,0A000h
mov es,ax
{ Рисуем точку с координатой CurrCoordinate и цветом CurrColor }
mov bx,ds:CurrCoordinate
mov al,CurrColor
mov byte ptr es:[bx],al
end;
end;
end;
{ Увеличиваем текущий размер квадрата }
inc(MyScreenBarSize);
{ Если размер стал более чем MaxBarSize }
if MyScreenBarSize>=MaxBarSize then MyScreenBarSize:=1;
{ Проверяем кейбоард }
until keypressed;
{ Считываем клавишу ~ readkey }
readkey;
asm
{ Восстановим старый обработчик прерывания 1Ch }
{ Адрес старого обработчика хранили в ячейке SaveOldHandler }
{ Получим его в ds:dx, ds сохраняем }
push ds { Сохранить регистр ds }
lds dx,ds:SaveOldHandler
{ Обратились к DOS, чтобы он восстановил старый обработчик }
mov ax,251Ch
int 21h
pop ds { Восстановить регистр ds }
{ Устанавливаем текстовый режим монитора }
mov ax,0003h
int 10h
end;
end.
|