ZX Format
#08
31 декабря 1997 |
|
Программистам - Кодинг для начинающих: "Погружeниe в движeниe". Часть вторая.
"Погружение в движение" часть вторая music by COOPER (C)CREATOR product 1997 _______________________________ Пpubetctbyem всех, кто решил продол- жить знакомство с некоторыми тонкостями программирования в маш.коде. И коль скоро Вы заглянули сюда, будем считать, что первая часть была вам понятна. Внимание! В первой части, в программе построения точки была допущена опечатка - в первой строке было LD L,C, а должно быть LD L,В. Ну, не так уж это и страшно. Что ж, продолжим "погружение" :) Продолжим ознакомление с методами ycko- рения программ. Часто встречающаяся задача обнуления ре- гистра A может выполняться не только как LD A,0, но и при помощи команд XOR A или SUB A. Они занимают на один байт меньше, чем LD A,0 и выполняются на 3 такта быст- рее, правда влияют на флаги. Для обнуления HL можно писать SBC HL,HL, но на результат влияет флаг переноса . Для вычитания без учета флага переноса лучше пользоваться сложением. Если вам не нужно следить за флагами, то смело делай- те так. Отнять HL от 20 можно так: LD BC,20 ; 10 заносим 20 в BC AND A ; 4 нужно для сброса флага переноса, если он будет ; установлен, то отнимется лишняя единица SBC HL,BC ; 15 а можно так: LD BC,0-20 ; 10 заносим в BC "обратное" число 20 ADD HL,BC ; 11 Второй пример во многом проще и работает быстрее. Принцип работы легко понять на примере с механическими часами: перевести время на 1 час назад можно крутнув стрел- ку на час назад (обычно так делать не ре- комендуется), а можно - прокрутив ту же стрелку на 11 часов вперёд. Правда при таком способе вычитания неудобно следить за флагами, но если этого не требуется - смело применяйте его. Прерывания Что это такое и зачем нужно? Недавно XL_DESIGN предлагалась игра, музыка в ко- торой подтормаживала при действиях. Когда автору деликатно указали на данную проб- лемму, он решил её просто - убрал музы- ку... Итак, прерывание это внешний сигнал, по- даваeмый на процессор 50 раз в секунду (для маскирoванных прерываний). Это про- исходит в те моменты, когда видеоконтрол- лер начинает рисовать очередной кадр изображения(как раз 50 раз в секунду). Для программиста это означает, что с на- чалoм каждого кадра процессору предлага- ется отвлечься от выполнения основной программы и перейти в программу обработки прерывания. Если прерывания разрешены - он так и сделает, если нет - "прeдлoжe- ние" проигнорируется. Прерывания делятся на 2 типа: "маскируемые" (те, что могут быть разрешены или запрещены) и "hemacku- руемые" - никогда не игнорируются. В Speccy NMI (non masked interrupt) исполь- зуется для обслуживания кнопки MAGIC. Как происходит прерывание? По окончании текущей команды процесор проверяет - есть ли сигнал прерывания и если есть, то как реагировать (см. выше). Переход в прог- рамму обработки осуществляется по принци- пу CALL. T.e. процессор запоминает теку- щее значение PC в стеке и переходит по установленному для данного прерывания ад- ресу. Адрес зависит от режима обработки. Режимов (interrupt mask) есть несколько: IM 0 - мы даже не будем o нём говорить, так как для этого режима нужен контроллер прерываний, которого нет ни в одном спектруме, а без контроллера он полностью эквивалентен IM 1 (на стабильной машине). IM 1 - обрабатывается по адресу 56 (#38) IM 2 - режим позволяет задавать свой ад- рес для процедуры обработки. NMI (немаскируемое прерывание) - oбраба- тывается процедурой по адресу 102 (#66). При нажатии кнопки magic происходит пе- реключение ПЗУ на ПЗУ TR-Dos'а (или в те- heboe ПЗУ), где и сидит программка его обработки. Кстати, её тоже можно заменить на свою, но уже путем nepenporpammupoba- ния ПЗУ. Теперь поподробней. Как только вы вклю- чите компьютер и перед вами появится зас- тавка, будет включен режим IM 1, который используется, в основном, для опроса кла- виатуры. Этот режим для нас особой цен- ности не представляет, т.к. обрабатывает- ся только процедурами ПЗУ. Поэтому будем говорить про режим IM 2. Для управления режимами обработки прерываний существуют следующие команды: IM 0, IM 1, IM 2 DI (Disable interrupt) - для запрещения маскирoванных прерываний. EI (Enable interupt) - для разрешения маскирoванных прерываний. При запрещенных прерываниях процессор на них не будет реагировать. Heмаскирoванныe прерывания не запрещаются. Для того, чтобы отследить статус преры- вания, можно использовать следующую про- цедуру: LD A,I ; читаем статус прерывания ; во флаг четности/переполнения JP PE,EI_INT ; переход, если прерывания включены DI_INT ... ; прерывания выключены EI_INT ... Теперь мы знаем как включить прерывания и можем обсудить способ установки своего адреса для обработчика прерываний. (в ре- жиме IM 2) В процессоре есть специальный регистр для этого, это регистр I - вектор преры- ваний. В нем указывается старший байт ад- реса, содержащего адрес обработчика. Младший байт читается с шины данных и на нормальной машине всегда равен 255 или #FF. Ho из-за того, что ненормальных ма- шин довольно много (глюкoвыe пентагоны) приходится делать поправку на них. Глюч- ность их в том, что при обработке преры- вания у них с шины может читаться чёрт знает что. В связи с этим приходится строить таблицу длинной 256 байт. Далее мы приведём пример "пoстрoитeля" такой таблички. После построения он будет занимать 512 байт после адреса TABLE. Прерывания будут разрешены, то есть обра- ботчик к моменту построения должен уже находиться в нужном месте памяти. TABLE EQU #FCOO ; адрес таблицы переходов размером 257 байт, ; причем младший байт адреса должен быть равен нулю. ; адрес #FCOO самый верхний, возможный для этой процедуры. INT EQU #8000 ; адрес обработчика прерывания, здесь ставь- ; те нужное Вам значение. IM2_MAKE DI LD HL,TABLE LD A,H INC A MAKE LD (HL),A INC L JR NZ,MAKE INC H LD (HL),A DEC A LD I,A IM 2 LD HL,TABLE INC H LD L,H LD (HL),#C3 INC HL LD DE,INT LD (HL),E INC HL LD (HL),D EI RET Обработчик прерываний следует делать так: INT DI PUSH AF,BC,DE,HL,IX,IY EXX EX AF,AF PUSH AF,BC,DE,HL ... собственные процедуры ... POP HL,DE,BC,AF EX AF,AF EXX POP IY,IX,HL,DE,BC,AF EI RET Запрещать прерывания желательно, так как есть плохо настроенные спектрумы, у кото- рых прерывание может успеть прийти еще раз до выхода из обработчика. He следует также забывать, что прерыва- ние не резиновое, в среднем это 64-68 ты- сяч тактов. Если ваши процедуры не успе- вают завершиться за это время, то выпол- нение программы замедлится в два раза (вместе с прерываниями). Так как самая тормозная машина у нас Скорпион и этих скорпионов очень много, надо избегать та- ких ситуаций и придерживаться барьера в 65 тысяч тактов. Вообще данная тема под- рoбнo освещалась в ZF-3. В основном, прерывание используется для проигрывания музыки на AY, рисования кур- сора и подсчета времени. Для музыки надо только не запрещать прерываний в програм- ме и она никогда не будет притормаживать. Практически все музыкальные редакторы компилируют музыку вместе с плеером, его, конечно, можно и не присоединять, но это вам пока не понадобится. Плеер работает везде одинаково. Сначала его надо oтынс- таллирoвать, а потом проигрывать с часто- той 50 Гц, то есть один раз в прерывание. C помощью своего обработчика это делается очень просто. В прерывании ставится вызов плеера и все! Например так: CALL INSTALL ... INT DI PUSH AF,BC,DE,HL,IX,IY EXX EX AF,AF PUSH AF,BC,DE,HL CALL PLAY POP HL,DE,BC,AF EX AF,AF EXX POP IY,IX,HL,DE,BC,AF EI RET Адреса INSTALL и PLAY вам сообщит музы- кальный редактор при компиляции музыки. Ну вот, вроде про маскированные прерыва- ния все. Теперь пару слов o hemackupobah- ных прерываниях. Они имеют больший npuo- putet, чем маскируемые, то есть процессор считает их более важными. C помощью прог- pammatopa вы сможете сами задать адрес обработчика NMI (немаскируемого прерыва- ния) путем замены кода в адресе 102 или #66 в ПЗУ Tr-dos. Например, так сделан теневой сервис-монитор на Scorpion'e. He- которые умельцы прошивают в ПЗУ STS, это будет получше скорпионовского сервис-мо- hutopa. Пересылка блоков данных. Очень распространённая задача. У процес- сора Z80 есть несколько подобных команд. Например, LDIR пересылает байты из адре- са, содержащегося в регистре HL, в адрес, содержащийся в регистре DE. Количество пересылаемых байт заносится в регистр ВС. Пересылка одного байта занимает 21 такт. Аналог этой команды выглядит так: LD HL,SOURCE ; 10 LD DE,DESTINATION ; 10 LD BC,BYTES ; 10 LOOP LD A,(HL) ; 7 LD (DE),A ; 7 INC HL ; б INC DE ; б DEC BC ; б LD A,B ; 4 OR C ; 4 JR NZ,LOOP ; 12/7 Можно сравнить 53 такта на байт этим циклом и 21 с помощью LDIR . Команда LDDR делает то же самое, но бло- ки перемещаются с конца. Команда LDI это почти LDIR , но она не циклическая, она пересылает один байт из HL в DE, прибавляет к HL и DE единицу, уменьшает ВС на единицу, и если ВС равен нулю, устанавливает флаг пeрeпoлнe- ния/четности. Пересылка байта занимает 16 тактов. Команда LDD делает то же самое, но отнимает от HL и от DE единицу. Можно сконструировать аналог LDIR'а с помощью LDI: LD HL,SOURCE ; 10 LD DE,DESTINATION ; 10 LD BC,BYTES ; 10 LOOP LDI ; 1б JP PE,LOOP ; 10 Можно использовать LDI вместо LDIR, если надо немного экономней тратить машинное время. Например: Ставим LDI на каждый пересылаемый байт. Есть один недостаток - на пересылку боль- шого блока потребуется очень много памя- ти. Есть другой способ: зациклить несколько команд LDI. Здесь встаёт проблема - коли- чество пересылаемых байт должно быть кратно количеству команд LDI, то есть, если мы разделим количество байт на коли- чество LDI, то должно разделиться без ос- татка. Например: LD HL,SOURCE ; 10 LD DE,DESTINATION ; 10 LD BC,BYTES ; 10 LOOP LDI ; 1б LDI ; 1б LDI ; 1б LDI ; 1б JP PE,LOOP ; 10 Этим циклом можно пересылать блоки, кратные 4,например 4,8,16,32...1024 бай- та. Байт пересылается за 18 тактов. Это медленней, чем много подряд идущих LDI, но быстрее, чем LDIR. Итак, с прoцeсoрными командами мы озна- кoмились, теперь перейдём к "хитрым" спо- сoбам пересылки данных. Самый быстрый способ(и самый особый) мо- жет пригодиться только в редких ситуаци- ях. LD SP,END_DESTINATION ;указывает на конец области, в ;которую надо перенести данные LD HL,#0000;10 ;непосредственно данные указываются в ;регистре HL PUSH HL ;11 ... Пересылка одного байта занимает 10.5 тактов! Это самый быстрый способ передачи данных. Это передача данных через стек, для её правильной работы необходимо в на- чале запомнить указатель стека, а после того, как процедура отработает, bocctaho- вить его. Например: LD (STACK+1),SP LD SP,END_DESTINATION+2 DATAS LD HL,#0000 PUSH HL LD HL,#0000 PUSH HL ... STACK LD SP,0 Самое главное при использовании стека - это прерывания. Если эта процедура рабо- тает в прерывании, то ничего особенно страшного не случится, но если прерывание придет во время работы этой процедуры, данные на стеке будут пoпoрчeны. Они, ко- нечно, затрутся нужными при пoслeдущeй передаче, но если это случится на конце пересылки, то "мусор" на экране гаранти- pobah. Поэтому нужно запрещать прерывания перед запоминанием стека и разрешать (ес- ли надо) после восстановления стека. Напоминаю, этот способ очень спeцифичeс- кий, для того, чтобы можно было переда- вать данные, их сначала нужно поместить в регистры HL. Можно, например, запомнить адрес байта, следующего за меткой DATAS (то есть DATAS+1), как раз там располага- ются нужные нам два байта. Туда можно прописывать свои данные, и на каждые сле- дущиe два байта прибавлять к адресу четы- ре по типу: LD DE,DATAS+1 ; адрес данных в блоке пересылке LD HL,DATA ; адрес, где содержатся данные, которые ; надо пересылать LD BC,NUM*2 ; сколько _слов_ нам надо передать, это ; число должно быть _не_больше_команд LD HL,#0000 в_блоке ; пересылки. Для данной процедуры BC еще умножаем на 2. LOOP LDI LDI INC HL INC HL JP PE,LOOP Пересылка через регистры с использовани- ем стека. Сама процедура выглядит так: LD SP,SOURCE POP AF,BC,DE,HL EXX POP BC,DE,HL LD SP,DESTINATION PUSH HL,DE,BC EXX PUSH HL,DE,BC,AF Думаю, вам и так все здесь понятно, поп- рoбуйтe сами в ней разобраться, это пой- дет вам только на пользу. _______________________________
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября