|
Amazing
#02
14 июля 1998 |
|
Ассемблер - Контроль времени (работа с прерываниями).

(C) 1998 by Mr.Beeper
Music by Ironman
Контроль времени (работа с прерываниями)
Прерывания с полным правом можно отнести к
наиболее мощным и интересным ресурсам
компьютера. К сожалению, работа с ними из
Бейсика абсолютно невозможна и именно
поэтому данный вопрос для многих из вас
может оказаться совершенно новым и неиз-
вестным. К настоящему моменту вы уже,
должно быть, достаточно освоились с ас-
семблером и надеемся, неплохо представ-
ляете, как работает микропроцессор, что
дает возможность понять, что будет нижепе-
речесляться. Для начала выясним, что же
собой представляют прерывания. Попробуем,
не вдаваясь в конструкторские тонкости,
объяснить принцип этого явления просто "на
пальцах". Когда вы находитесь в редакторе
Бейсика или TASM и размышляете над очеред-
ной строкой программы, компьютер не торо-
пит вас и терпеливо ожидает нажатия той
или иной кла виши. Может даже показаться,
что процессор в это время и вовсе не рабо-
тает. Но, как вы уже знаете, это не так.
Просто выполняется отдельная часть прог-
раммы,аналогичная процедуре WAIT: в цыкле
опрашивается системная переменная LAST_K и
когда вы нажимаете какую-то клавишу, код
ее появляется в ячейке 23560. Но, спраши-
вается, откуда он там берется ? Программа
ведь только читает ее значение, никак не
модифицируя ее содер жимое. А разрешается
эта загадка довольно просто. Дело в том,
что 50 раз в секунду процессор отвлекается
от основной программы и переключается на
выполнение специальной процедуры обработки
прерываний, рассположенной по адресу 56,
словно бы встретив команду RST 56 или CALL
56, только переход этот происходит не про-
граммным, а аппаратным путем. У процедуры
56, есть две основных задачи: опрос кла-
виатуры и изменение текущего значения тай-
мера (системная пере менная FRAMES - 23672
/73 /74). Результаты опроса клавиш также
заносятся в область системных переменных,
в частности, код нажатой клавиши помещает-
ся в LAST_K. После выхода из прерывания
процессор как ни в чем не бывало продол-
жает выполнять основную программу. В ре-
зультате получается довольно интерестный
эффект: создается впечатление,будто бы па-
раллельно работают два процессора, каждый
из которых выполняет не зависимую задачу.
Все это прекрасно, но какую пользу для
себя мы можем из этого извлечь? Ведь в ПЗУ
ничего не изменишь. Действительно, от пре-
рываний программистам было бы не много про
ку, если бы невозможно было переопределять
адрес процедуры для их обработки. Мы уже
говорили о существовании регистра I, назы-
ваемого регистром вектора прерываний, а
сейчас расскажем, какую роль он выполняет
в программах,использующих собственные пре-
рывания.
Прежде всего вам нужно знать, что сущест
вуют три различных режима прерываний. Они
обозначаются цифрами от 0 до 2.Стандартный
режим имеет номер 1,и о нем мы уже кое-что
сказали. Нулевой режим нам не интересен,
поскольку на практике он ничем не отли-
чается от первого (именно, на практике,по-
тому что на самом деле имеются су-
ществующие различия, но в ZX-Spectrum они
не реализованы). А вот о втором режиме
нужно поговорить более основательно. Сна-
чала скажем несколько слов о том, как он
работает и что при этом происходит в
компьютере. С приходом сигнала прерываний
процессор определяет адрес указателя на
процедуру обработки прерываний. Он состав-
ляет из байта, считанного с шины данных
(младший), который, собственно, и называет
ся <вектором прерывания> и содержимого ре-
гистра I (старший байт адреса). Затем на
адресную шину переписывается значение полу
ченного указателя, но предварительно преж-
нее состояние шины адреса заносится в стек
Таким образом, совершается действие, ана-
логичное каманды процессора CALL. Посколь-
ку в SPECCY вектор прерываний, как правило
равен 255, то на практике, адрес указателя
может быть определн только регистрм I. Для
этого его значение нужно умножить на 256 и
прибавить 255. Для установки нового обра-
ботчика прерыва ний, нужно выполнить ряд
действий. Перечес лим их в том порядке, в
котором они должны производится:
1. Запретить прерывания, так как есть ве
роятность того, что сигнал прерываний при-
дет во время установки, а это может привес
ти к нежелательным последствиям. Достигает
ся это выполнением команды процессора DI.
2. Записать в память по рассчитаному за-
ранее адресу указател на процедуру обработ
ки прерываний (то есть адрес этой процеду-
ры).
3. Задать в регистре вектора прерываний
I старший байт адреса указателя на обработ
чик.
4. Установить командой IM 2 второй режим
прерываний.
5. Вновь разрешить прерывания командой
EI.
Естественно, что к этому моменту, сама
процедура обработки прерываний должна имет
ся в памяти. Для возврата к стандартному
режиму обработки прерываний нужно выпол-
нить похожие действия:
1. Запретить прерывания.
2. Не помешает восстановить значение ре-
гистра I, записав в него число 63
3. Назначить командой IM 1 первый режим
прерываний.
4. Разрешить прерывания.
Несколько подробней нужно остановиться
на втором и третьем пунктах установки пре-
рываний. Предположим, что процедура обра-
ботчик находится по адресу 60000 (#EA60) и
память, начиная с 65000, никак в программе
не используется. Значит указатель можно по
местить именно в эту область. Для регистра
I в этом случае можно выбрать одно из двух
зна*ений: 253 или 254. Тогда для размеще-
ния указателя можно использовать либо адре
са 65023/65024 (253*256+255/256) либо
65279/65280 (254*256+255/256). Например,
при I равном 254 запишем по адресу 65279
младший байт адреса обработчика - #60, а в
65280 поместим старший байт - #ЕА. Однако
нужно учитывать, что некоторые внешние
устройства могут изменять значение вектора
прерывания. Кроме того, если ваш SPECCY
сработан не слишком добросовестным
производителем, то вектор прерывания иног-
да может скакать совершенно произвольным и
непредсказуемым образом. Принимая это во
внимание, даже во многих фирменных играх
используется несколько иной подход. Вместо
записи двух байтов по определенному адресу
выстраивается целая таблица размером как
минимум 257 байт с таким расчетом, чтобы
при любом значении вектора прерываний счи-
тывался один и тот же адрес. Понятно, что
для этого все байты таблицы должны быть
одинаковыми. Это несколько осложняет уста-
новку прерывания и требует больше памяти,
но зато значительно увеличивается надеж-
ность программы.
Наиболее удачным для такой таблицы пред-
ставляется байт 255 (#FF). В этом случае
обработчик прерываний должен находится по
адресу 65535 (#FFFF). На первый взгляд мо-
жет показаться странный выбор такого адре-
са, ведь остается всего один байт! Но и
этого единственного байта оказывается дос-
таточным, если в него переписать код коман
ды JR. Следующий байт, находящийся по ад-
ресу 0, укажет смещение относительного пе-
рехода. По нулевому адресу в ПЗУ записан
код команды DI (#F3), поэтому полностью ко
манда будет выглядеть как JR 65524. Далее
в ячейке 65524 можно разместить уже более
"длинную" команду JP addres и заданный в
ней адрес может быть совершенно произволь-
ным.
Приведем пример такой подпрограммы уста-
новки прерываний:
INT LD A,24 ;код команды JR
LD (#FFFF),A
LD A,195 ;код команды JP
LD (65524),A
LD (65525),HL ;в HL-адрес обработ-
;чика прерываний
LD HL,#FE00 ;построение таблицы
LD DE,#FE01 ;для векторов прер.
LD BC,256 ;размер табл.минус 1
LD (HL),#FF ;адрес перехода#FFFF
LD A,H ;запоминаем старший
;байт адреса таблицы
LDIR ;заполняем таблицу
DI ;запрещаем прер. на
;время установки вто
;рого режима прерыв.
LD I,A ;задаем в регистре I
;старший байт адреса
;таблицы для векто-
;ров прерываний
IM 2 ;вкл. второй режим
EI ;разрешаем. прерыв.
RET
Перед обращением к ней в регистровой па-
ре HL необходимо указать адрес соответству
ющей процедуры обработки прерываний. Учти-
те, что в области памяти, начиная с адреса
65024, менять что - либо не желательно.
Если все же возникает такая необходимость,
убедитесь прежде, что своими действиями вы
не затроните выстановленные процедурой бай
ты. Подпрограмма восстановления первого
режима выглядит заметно проще и в коммен-
тариях уже не нуждается:
INT2 DI
LD A,63
LD I,A
IM 1
EI
RET
При составлении процедуры обработки пре-
рываний нужно придерживаться определенных
правил. Во-первых, написанная вами подпрог
рамма должна выполняться за достаточно ко-
роткий промежуток времени. Желательно,
чтобы ее быстродействие было сопоставимо с
"пульсом" прерываний, то есть чтобы ее про
должительность не превышала 1/50 секунды.
Это правило не является обязятельным, но в
противном случае трудно будет получить эф-
фект "паралельности" процессоров. Во-
вторых, это уже совершенно необходимо, все
ре гистры, которые могут изменить свое
значение в вашей процедуре, должны быть
сохранены на входе и восстановлены перед
выходом. Это же относится и к любым пере-
менным используемым не только в прерыва-
нии,но и в основной программе. В связи с
этим не реко мендуется обращаться к
подпрограммам ПЗУ , по крайней мере, до
тех пор, пока вы не знаете совершенно точ-
но,какие в них используются регистры и
какие системные переменные при этом могут
быть изменены.Вызов под программ ПЗУ не
желателен и еще и потому, что некоторые из
них разрешаят прерывания, что совершенно
не допустимо, во избежание рекурсии (т.е.
самовызова) обработчика, ко торый должен
работать при запрещенных прерываниях. Од-
нако использовать команду DI в самом нача-
ле процедуры не обязательно, так как это
действие выполняется автоматически и вам
нужно только позаботится о разрешении пре-
рываний перед выходом.
Если вы не хотите лишаться возможностей,
предоставляемых стандартной процедурой об-
работки прерываний, можете завершить свою
подпрограмму командой JP 56. А при исполь-
зовании прерываний в бейсик-программах без
этого просто не обойтись, иначе клавиатура
окажется заблокирована.В общем случае обра
ботчик прерываний может иметь такой вид:
INT PUSH AF
PUSH BC
PUSH DE
PUSH HL
.......
POP HL
POP DE
POP BC
POP AF
JP 56
В заключении хочу предложить интерестную
прогамму, основаную на выше преведенном
тексте. Так что, запускайте TASM 4.0 и
"дерзайте" !
;LOADING WITH MUSIC ON 1 INTERRUPT OF CPU;
ORG #61A8
DI
CALL #C000 ; music addres !!!
LD HL,#FD00
LD DE,#FD01
LD BC,#0100
LD (HL),#FE
LDIR
LD A,#C3
LD (#FEFE),A
LD HL,INT
LD (#FEFF),HL
LD A,#FD
LD I,A
IM 2
EI
CYCL LD B,53
LD D,TRK
LD E,SEC + 1 ;<<<<<<<< Warning !
LD HL,26000
CALL LOAD
JR C,CYCL
EXIT DI
LD A,#3F
LD I,A
IM 1
EI
CALL #C000
RET
INT DI
PUSH HL
PUSH DE
PUSH BC
PUSH AF
PUSH IX
PUSH IY
EX AF,AF'
EXX
PUSH HL
PUSH DE
PUSH BC
PUSH AF
CALL #C006 ; music addres !!!
POP AF
POP BC
POP DE
POP HL
EXX
EX AF,AF'
POP IY
POP IX
POP AF
POP BC
POP DE
POP HL
EI
JP #3D2F
LOAD LD (LL62F6),SP
LD A,(#5CF6)
LD (LL6284),A
LD A,#FB
LD (LL62BD),A
LL6275 LD A,D
AND A
RRA
LD C,#7F
CALL LL62DC
LD A,#3C
JR NC,LL6283
LD A,#2C
LL6283 OR #00
LL6284 EQU $-1
LD C,#FF
CALL LL62DC
LD A,#18
PUSH BC
PUSH DE
LL628E LD BC,#011F
CALL LL62DC
LD D,A
LD IX,#20B1
CALL LL62E0
POP DE
POP BC
LL629E PUSH BC
PUSH DE
LD A,E
LD C,#5F
CALL LL62DC
LL62A6 PUSH HL
LD C,#1F
LD A,#80
CALL LL62DC
LD C,#7F
LD IX,#3FD5
CALL LL62E0
JP P,LL62F5
CALL LL62E5
LL62BD NOP
POP HL
LD A,(#5CD6)
OR A
JR NZ,LL62A6
INC H
POP DE
POP BC
BIT 4,E
JR NZ,LL62D1
INC E
DJNZ LL629E
JR LL62D6
LL62D1 LD E,#01
INC D
DJNZ LL6275
LL62D6 LD (#6264),DE
AND A
RET
LL62DC LD IX,#2A53
LL62E0 PUSH IX
JP #3D2F
LL62E5 XOR A
LD (#5CD6),A
LD HL,LL628E
PUSH HL
INC A
PUSH AF
LD IX,#2099
JR LL62E0
LL62F5 LD SP,#0000
LL62F6 EQU $-2
SCF
RET
Другие статьи номера:
Похожие статьи:
В этот день... 17 ноября