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
Другие статьи номера:
Похожие статьи:
В этот день... 10 ноября