ZX Club #09
31 октября 1998

Soft Group - Интересный текст от Ивана Рощина для кодеров.

<b>Soft Group</b> - Интересный текст от Ивана Рощина для кодеров.
                                
CODING                                    
                                
                                
Интересный текст от Ивана Рощина для коде-
ров. Пожелания,  предложения,  возражения,
отправляйте на мой адрес: 2:5020/689.53   
                                
20 ноября 98                   Alex Letaev
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(c) Иван Рощин, Москва, 13.11.1998        
                                
Fido  : 2:5020/689.53                     
E-mail: asder_ffc@softhome.net            
                                
   +---------------------------------+    
   | Недокументированная особенность |    
   |         процессора Z80          |    
   +---------------------------------+    
                                
Разрешается свободное распространение этой
статьи при условии  не внесения каких-либо
изменений и сохранения моего копирайта.   
                                
1. С чего все началось                    
----------------------                    
                                
Писал я как-то  очередную версию программы
BestView (v2.4),  и использовал в ней  вот
такой фрагмент:                           
                                
        ....                              
        EI                                
        CALL    SUBR1                     
        HALT                              
        ....                              
                                
SUBR1   LD      A,R                       
        PUSH    AF                        
        DI                                
                                
        ....                              
                                
        POP     AF                        
        DI                                
        RET     РО                        
        EI                                
        RET                               
                                
В этом фрагменте происходит вызов процеду-
ры SUBR1,  которая на время  своей  работы
запрещает прерывания, а при выходе восста-
навливает прежний режим их работы.        
                                
Проверка  того,  разрешены  или  запрещены
прерывания при вызове процедуры, и восста-
новление режима прерываний происходит сле-
дующим образом:                           
                                
-команда LD A,R  заносит во флаг Р/V  сос-
 тояние триггера прерываний IFF2;         
-регистровая пара AF  запоминается в стеке
 (PUSH AF);                               
-запрещаются прерывания (DI);             
-выполняются, собственно,  те функции, для
 кот. и предназначалась процедура SUBR1;  
-содержимое AF восстанавливается (POP AF);
-прерывания запрещаются (DI);             
-если флаг Р/V сброшен,  происходит  выход
 из процедуры  с запрещенными прерываниями
 (RET РО);                                
-иначе  происходит  выход  с  разрешенными
 прерываниями (EI: RET).                  
                                
Я стал  замечать,  что  при  работе  этого
фрагмента BestView зависает - не всегда, и
даже  не слишком часто,  а в очень  редких
случаях. Но все равно это было не очень-то
приятно. Программа, вроде бы, не содержала
никаких ошибок,  по крайней мере с первого
взгляда  ничего подозрительного я не заме-
тил.  Оставалось лишь  прибегнуть  к более
сильным средствам...                      
                                
2. Ситуация начинает проясняться          
--------------------------------          
                                
После очередного зависания  я вставил чис-
тый диск и уверенно  нажал  кнопку  MAGIC.
Затем загрузил отладчик "STS 6.2  +@"  (не
зря я его переделывал - теперь  с его  по-
мощью после загрузки @-файла можно восста-
новить содержимое регистров процессора  на
момент сброса программы на диск).  Нажатие
пары клавиш - и вот я вижу, в каком  месте
программы произошло зависание.            
                                
        ....                              
        EI                                
        CALL    SUBR1                     
        HALT <------------ вот здесь!     
        ....                              
                                
Типичный случай - прерывания запрещены,  и
процессор прекратил  выполнение  программы
на команде HALT. Но почему прерывания ока-
зались запрещены - неясно.  Ведь перед вы-
зовом процедуры SUBR1  они  были разрешены
командой  EI,  а  после  окончания  работы
SUBR1  они тоже  должны быть  разрешены  -
процедура SUBR1  не должна оказывать влия-
ния на режим их работы.                   
                                
Трассирую SUBR1. Все идет как положено - и
при входе, и при выходе прерывания остают-
ся  разрешенными.  Повторяю   трассировку:
раз, другой,.. десятый.  Все идет нормаль-
но.                                       
                                
А может быть,  дело  в том,  что  в  SUBR1
что-то происходит со стеком? И из-за этого
иногда неправильно  восстанавливается  со-
держимое AF? Надо бы проверить...         
                                
3. Зависания: дубль второй                
--------------------------                
                                
Ну вот,  переделал  программу.  Теперь  уж
точно буду знать, в чем дело:             
                                
SUBR1   LD      A,R                       
        PUSH    AF                        
        DI                                
                                
        PUSH    HL                        
        PUSH    AF                        
        POP     HL                        
        LD      (WR_НН1),HL               
        POP     HL                        
                                
        ....                              
                                
        POP     AF                        
                                
        PUSH    HL                        
        PUSH    AF                        
        POP     HL                        
        LD      (WR_НН2),HL               
        POP     HL                        
                                
        DI                                
        RET     РО                        
        EI                                
        RET                               
                                
WR_НН1  DW  0                             
WR_НН2  DW  0                             
                                
Содержимое AF теперь запоминается не толь-
ко в стеке, но и в переменной WR_НН1  (для
контроля),  а  при  выходе  из процедуры -
снятое со стека  значение  запоминается  в
WR_НН2. Если процедура работает правильно,
WR_НН1 и WR_НН2 должны совпадать,  а  флаг
Р/V быть установленным.                   
                                
Запускаю...  Вот уже минуту BestView рабо-
тает нормально...  Просматриваю  с ее  по-
мощью тот самый файл,  при просмотре кото-
рого она зависла в прошлый раз...  Ну вот,
опять!  Да и неудивительно, ведь причину я
не устранил. Ладно, будем разбираться.    
                                
Снова нажимаю MAGIC, загружаю "STS 6.2 +@"
и сразу  же  проверяю  значения  WR_НН1  и
WR_НН2. И там, и там записано #5908.  Зна-
чения совпадают - следовательно, при рабо-
те со стеком ошибок не было. Но если в ре-
гистре флагов  содержится  #08  -  значит,
флаг Р/V сброшен,  и при вызове  процедуры
SUBR1 прерывания были запрещены.          
                                
Но это же  совершенно  невозможно!  Ведь в
программе стоит EI: CALL SUBR1!  Наверное,
просто Спектрум перегрелся, и потому такие
глюки.  Ничего более умного  я в этот день
так и не придумал.                        
                                
4. Ложный след                            
--------------                            
                                
На следующий день я нашел возможное объяс-
нение таинственному запрещению прерываний.
Допустим, после команды EI, но до выполне-
ния команды LD A,R  произошло  прерывание.
Как известно, процедура его обработки дол-
жна заканчиваться командами EI:RET (потому
что в начале обработки  происходит автома-
тическое  запрещение прерываний).  Если же
обработчик прерываний  завершается  просто
командой RET, то прерывания останутся зап-
рещенными.                                
                                
Конечно, вероятность того,  что прерывание
произойдет именно  между командами EI и LD
A,R очень мала, но ведь и зависания проис-
ходят очень редко.  Так что это лишний раз
подтверждало мою гипотезу.                
                                
Тем не менее,  оставалось неясным,  с чего
бы это  обработчик  прерываний  завершался
командой RET, а не EI:RET.  Я решил прове-
рить, действительно ли в этом все дело,  и
для этого добавил после  EI  команду  HALT
(см.  ниже).  Если  обработчик  прерываний
действительно завершается некорректно,  то
после добавленного HALT-а прерывания всег-
да  будут  запрещены,  и,  соответственно,
BestView всегда будет зависать.           
                                
        ....                              
        EI                                
        HALT <------ добавленная команда  
        CALL    SUBR1                     
        HALT                              
        ....                              
                                
                                
SUBR1   LD      A,R                       
        PUSH    AF                        
        DI                                
                                
        ....                              
                                
        POP     AF                        
        DI                                
        RET     РО                        
        EI                                
        RET                               
                                
Компилирую, запускаю...  Совершенно неожи-
данный  результат  -  зависания  полностью
прекратились! Хотел все так и оставить, но
все же решил разобраться, почему так полу-
чается.                                   
                                
5. Прием "упрощение программы"            
------------------------------            
                                
Когда найти ошибку обычными средствами  не
удается, я удаляю  из  программы  все  что
можно,  но чтобы ошибка  при этом  остава-
лась. В итоге, когда от программы остается
с десяток строк, ошибка заметна сразу. Так
я поступил и в этот раз:                  
                                
        ORG     #6000                     
                                
        EI                                
                                
M1      CALL    SUBR1                     
        JR      M1                        
                                
SUBR1   LD      A,R                       
        DI                                
        JP      РО,M2                     
        EI                                
        RET                               
M2      LD      A,4                       
        OUT     (254),A                   
        RET                               
                                
                                
Вот такая программа, всего 19 байт. Разре-
шаются прерывания,  и в бесконечном  цикле
вызывается процедура SUBR1.  Эта процедура
устанавливает  зеленый  border,  если  при
входе в нее  прерывания были запрещены,  и
не меняет  цвет  border,  если  прерывания
разрешены.  Таким образом, если произойдет
самопроизвольное  запрещение   прерываний,
это сразу же будет заметно.               
                                
Запускаю - да,  border меняет свой цвет на
зеленый. Причина столь странного поведения
программы  остается  неизвестной.    Может
быть,  в этом виновата процедура обработки
прерываний 1-го рода? Добавляю к программе
несколько команд, устанавливающих режим IM
2 с обработчиком,  состоящим всего из двух
команд: EI:RET.                           
                                
        ORG     #6000                     
                                
        LD      HL,#8000                  
        LD      (HL),#81                  
        LD      DE,#8001                  
        LD      ВС,#100                   
        LDIR                              
        LD      A,#80                     
        LD      I,A                       
        IM      2                         
                                
        EI                                
                                
M1      CALL    SUBR1                     
        JR      M1                        
                                
SUBR1   LD      A,R                       
        DI                                
        JP      РО,M2                     
        EI                                
        RET                               
M2      LD      A,4                       
        OUT     (254),A                   
        RET                               
                                
        ORG     #8181                     
                                
        EI                                
        RET                               
                                
Запускаю - тот  же  результат!  Хотя   при
трассировке и этой, и предыдущей программы
в отладчике border остается черным. Внима-
тельное  изучение  программы  приводит   к
предположению: может быть, команда LD  A,R
иногда устанавливает бит Р/V так, как буд-
то прерывания запрещены, в то время как на
самом деле они разрешены?                 
                                
6. Неужели ошибка в процессоре?           
-------------------------------           
                                
Еще раз изменяю программу. Теперь прерыва-
ния вообще  не будут  запрещаться  (убрана
команда DI).  Если при выполнении  команды
LD A,R бит Р/V станет равным 0, на некото-
рое время border станет зеленым (для этого
предусмотрена задержка):                  
                                
        ORG     #6000                     
                                
        LD      HL,#8000                  
        LD      (HL),#81                  
        LD      DE,#8001                  
        LD      ВС,#100                   
        LDIR                              
        LD      A,#80                     
        LD      I,A                       
        IM      2                         
                                
        EI                                
                                
M1      CALL    SUBR1                     
        JR      M1                        
                                
SUBR1   LD      A,R                       
        RET     РЕ                        
                                
        LD      A,4                       
        OUT     (254),A                   
        LD      HL,0                      
        LD      DE,0                      
        LD      ВС,#600                   
        LDIR             ;WAIT            
        XOR     A                         
        OUT     (254),A                   
        RET                               
                                
        ORG     #8181                     
                                
        EI                                
        RET                               
                                
Запускаю...  И что я вижу?  Верхняя  часть
border-а мигает зеленым цветом:           
                                
Это говорит о том, что, во-первых, команда LD A,R действительно иногда неверно уста- навливает бит Р/V, и во-вторых - что это происходит в момент прихода прерывания, а не когда угодно (действительно, тогда бы
border мигал зеленым в совершенно произ- вольных местах). То, что верхняя часть border-а мигает, а не постоянно окрашена в зеленый цвет, тоже получает свое объяснение. По-видимому, ко- манда LD A,R неправильно работает лишь тогда, когда импульс прерывания приходит во время ее выполнения, а так бывает дале- ко не всегда - прерывание может произойти и во время выполнения другой команды. 7. Окончательное подтверждение ------------------------------ Проверим этот факт. Пусть обработчик пре- рывания определяет, в каком месте была прервана программа. Если она была прервана именно после команды LD A,R, пусть бордюр на некоторое время станет желтым: ORG #6000 LD HL,#8000 LD (HL),#81 LD DE,#8001 LD ВС,#100 LDIR LD A,#80 LD I,A IM 2 EI M1 CALL SUBR1 JR M1 SUBR1 LD A,R ВР1 RET РЕ LD A,4 OUT (254),A LD HL,0 LD DE,0 LD ВС,#600 LDIR ;WAIT XOR A OUT (254),A RET ORG #8181 EXX EX AF,AF' POP HL PUSH HL LD DE,ВР1 AND A SBC HL,DE JR NZ,NE_ВР1 LD A,6 OUT (254),A LD HL,0 LD DE,0 LD ВС,#600 LDIR ;WAIT NE_ВР1 EXX EX AF,AF' EI RET Если неправильная работа команды LD A,R не связана с тем, что во время ее выполнения приходит импульс прерывания, то мы увидим, как верхняя часть border-а будет мигать то зеленым цветом, то желтым. Но если связь между этими двумя событиями существует, то мы должны увидеть, как верхняя часть border-а мигает желтым цветом, а нижняя часть - зеленым, причем они должны мигать совершенно синхронно. Запускаю - и вижу именно то, что и предпо- лагал. Действительно, такая связь сущест- вует:
Но как могут быть связаны прерывания и ра- бота команды LD A,R? Эта команда помещает во флаг Р/V содержимое триггера прерываний IFF2. При разрешенных прерываниях этот триггер равен 1, а когда приходит импульс прерывания, он автоматически сбрасывается
в 0, чтобы исключить повторную обработку прерывания. Но обработка запроса на преры- вание начинается во время выполнения пос- леднего такта выполняемой команды (т.е. команды LD A,R). И, видимо, уже сброшенный триггер IFF2 копируется во флаг Р/V (дей- ствительно, с точки зрения процессора, прерывания в этот момент уже запрещены). Все сказанное относится и к команде LD A,I. Приведенная информация была проверена на оригинальном процессоре Z80 фирмы ZILOG и на отечественном аналоге КР1858ВМ1. 8. К чему это приводит и что делать? ------------------------------------ Применение команд LD A,R и LD A,I для оп- ределения состояния триггера прерываний, вообще говоря, используется во многих про- граммах (и даже в ПЗУ TR-DOS). Вот вам и объяснение некоторого числа странных зави- саний. Кажется, будто вероятность прихода импульса прерывания именно во время выпол- нения команды LD A,R невелика. Но, во-пер- вых, вероятность увеличивается за счет то- го, что эта команда может выполняться в программе не один раз (а для зависания достаточно единственного неверного выпол- нения), и, во-вторых - если в программе до этого встречалась команда HALT, т.е. син- хронизация с прерываниями, может случиться так, что команда LD A,R будет каждый раз выполняться в то время, когда наиболее ве- роятно очередное прерывание (так и было в BestView). Итак, этот способ ненадежен. Но как же быть? Оказывается, можно со 100% точностью определять состояние триггера прерываний по следующему простому правилу: -выполнить команду LD A,R; -если флаг Р/V = 1 - значит, прерывания в самом деле разрешены; -если флаг Р/V = 0 - либо прерывания в са- мом деле запрещены, либо они разрешены, но команда LD A,R неверно установила флаг. Чтобы устранить неопределенность, еще раз выполняем команду LD A,R. Если и теперь флаг Р/V = 0 - значит, прерывания запрещены (в самом деле, не может быть так, чтобы и во время выполнения второй команды LD A,R произошло прерывание - между двумя прерываниями проходит 1/50 секунды, а между двумя командами LD A,R - гораздо меньше времени). Если же флаг Р/V = 1 - значит, прерывания разрешены. Вот соответствующий фрагмент программы: SUBR1 LD A,R JP РЕ,M1 LD A,R M1 PUSH AF DI .... POP AF DI RET РО EI RET 9. Как это можно использовать? ------------------------------ С помощью команды LD A,R удобно выполнять тестирование процессора, чтобы распознать выполнение программы под эмулятором. Эму- лятор выполняет команды Z80 последователь- но, одну за другой, и команда LD A,R всег- да будет правильно устанавливать флаг Р/V. А в реальном Z80 это не так. Вот простейшая процедура для тестирования процессора, которая возвращает в аккумуля- торе 1, если она запущена на реальном Z80, и 0 в противном случае. Она пытается 65536 раз прочитать регистр R при разрешенных прерываниях, и если при этом хотя бы один раз флаг Р/V установится в 0 - делается вывод, что процедура работает на реальном Z80. TESTZ80 EI LD ВС,0 M1 LD A,R JP РО,QUIT INC ВС LD A,В OR С JR NZ,M1 RET QUIT LD A,1 RET Если распознался эмулятор, можно либо прекращать выполнение программы (своеоб- разная защита), либо отключать некоторые участки программы, которые могут неверно работать под эмулятором (например, вместо прямой работы с ВГ93 использовать точку входа #3D13 и т.п.). 10. Исправление STS 6.2 ----------------------- В известном отладчике STS определение сос- тояния триггера прерываний также происхо- дит с помощью команды LD A,R. Из-за этого может неправильно выполняться трассировка программы. При трассировке STS запускает каждую команду (кроме команд передачи уп- равления) с помощью резидента, а после окончания ее выполнения запоминает содер- жимое регистров процессора и состояние триггера прерываний. Вот тут и возможны ошибки. Допустим, прерывания разрешены и трасси- руется такая простейшая программа: #8000 NOP #8001 JR #8000 Прекратив трассировку через некоторое вре- мя (при отключенной опции Indicate одной минуты вполне достаточно), мы увидим, что прерывания оказались запрещены. Если бы трассировалась реальная программа, такое запрещение прерываний могло оказать влия- ние на весь ход ее дальнейшего выполнения и даже привести к зависанию (если при трассировке попалась бы команда HALT). Совершенно очевидно, что в STS надо внести исправления. Вот как это сделать для вер- сии 6.2: Сначала нужно запустить STS и загрузить файл "stsб.2 <С>", в котором и будут производиться исправления. Затем нужно отыскать свободные 14 байт - их назначение будет объяснено ниже. Можно использовать буфер функции пользователя (с адреса #FEЗ7). Но в той версии STS, кото- рую я использую, этот буфер занят под про- цедуру дизассемблирования с метками ассем- блера ZX ASM, поэтому я решил сократить некоторые текстовые сообщения: 'Block' -> 'Bl.' (экономится 2 байта) 'Save' -> 'S.' (----/----- 2 --/--) 'Load' -> 'L.' (----/----- 2 --/--) ' DEFB' -> ' ' (----/----- 4 --/--) 'FileName' -> 'Name' (----/----- 4 --/--) Для этого с адреса #ЕВ24 нужно ввести сле- дующую последовательность байт: #ЕВ24: AE 46 72 6F ED 54 EF 46 #ЕВ2С: 69 6С E5 53 65 63 74 6F #EB34: F2 53 AE 4С AE 53 74 6F #EB3C: 70 20 69 E6 42 61 6E ЕВ #EB44: 51 75 69 F4 54 72 61 63 #EB4C: E5 53 74 61 72 F4 44 69 #EB54: 73 61 73 ED A0 46 69 6С #EB5C: E5 42 41 53 49 С3 20 44 #EB64: 4F D3 По адресу #E702 заменяем значение #0A на #0E, чтобы правильно печаталось имя файла (т.к. вместо строки FileName осталось просто Name). Итак, теперь с адреса #EB66 свободно 14 байт. Смотрим, где в STS происходит опре- деление состояния триггера прерываний: #DFFE: LD (#5BA1),SP LD SP,#5BA1 PUSH ВС PUSH AF LD A,R DI LD ВС,#7FFD LD A,#1F OUT (С),A LD В,#BF LD A,#00 OUT (С),A JP #E028 Заменим команды LD A,R: DI на NOP, а ко- манду JP #E028 - на JP #EB66. С адреса #EB66 поместим такой фрагмент: #EB66: LD A,R JP РО,#EB6E + NOP | +- JR #ЕВ70 | | LD A,R <---+ +> DI JP #E028 Обратите внимание - этот фрагмент в любом случае при своей работе увеличивает ре- гистр R на одинаковую величину (на 7). Де- ло в том, что далее будет выполнена еще одна команда LD A,R, на этот раз нужная уже для определения значения регистра R, и будет произведена коррекция полученного значения, т.к. значение регистра R увели- чивается с каждой выполненной командой, а надо узнать его значение на момент оконча- ния выполнения трассируемой команды. Вот как это выглядит: #DCA2: LD A,#5A LD HL,#FEFЧ SLA (HL) RLA ADD A,(HL) RRCA LD (HL),A RET Константу #5A по адресу #DCA3 следует за- менить на #53, т.е. уменьшить на 7 - ведь в программу был добавлен дополнительный фрагмент, увеличивающий регистр R на 7, и надо скомпенсировать это изменение. После этого остается только записать изме- ненный файл на диск.



Другие статьи номера:

От редакции - Перспективы развития оболочки.

Soft Group - Бегущие строки вне экрана.

Soft Group - Интересный текст от Ивана Рощина для кодеров.

Hard Group - Доработка Pentagon 128k до 512k.

Hard Group - Kempston mouse.

User Group - Обзор утилит из Сибири.

User Group - РT: шаг вперёд или назад?

Pot pourri - Welcome: Интервью.

Info - Каталог системных, прикладных и обучающих программ фирмы "Kоmel".

Info - Объявления. Реклама.

Info - Анкета ZX-CLUB.

Party - Funtop: On The Eve.

Party - Funtop: Первые Ласточки. Очерк.

Party - Funtop: Репортаж Сергея Новикова - День первый: Compo.

Party - Funtop: Репортаж Сергея Новикова - День второй: After Funtop.

Party - Funtop: Репортаж Сергея Новикова - Из Несказанного.

Party - Funtop: Total table.

Party - Funtop: Briefly.

Party - Chaos Constructions: Правила фестиваля.

Party - Chaos Constructions: Когда утихли страсти.

Party - Chaos Constructions: Предмет спора.

Party - Art Comp: Раскрутка.

Party - Art Comp: Свершилось.

Party - Art Comp: Завершающий этап.

Party - Dоxycоn-98.

Party - Complex Compo.

X-Files - Введение.

X-Files - Типы НЛО.

X-Files - Характерные черты.

X-Files - Свойства НЛО.

X-Files - Воздействие НЛО на почву и растительность.

X-Files - Воздействие НЛО на технику.

X-Files - Воздействие НЛО на здания, животных, людей.

X-Files - НЛО из атмосферы в гидросферу.

X-Files - НЛО под водой.

X-Files - НЛО в космосе.

X-Files - Действия НЛО на объекты.

X-Files - Взаимосвязаные действия НЛО.

X-Files - Появления НЛО во время событий.

X-Files - Реакция НЛО на попытки захвата.

X-Files - Стремление НЛО к контакту.

Набат - Дискографии рок-групп.

Набат - Конспект: Грани Агни Йоги.

Toys - Наш анонс: Пыль звёздных дорог.

Toys - Пароли и советы.

Enjoy - Stars: Романтичный констебль Бентон.

Enjoy - Будем знакомы. Случай из жизни.

Enjoy - Военка. Объявленьице. Анекдоты.

Enjoy - Чернушка. Акулы пера. Computer humor.

Elders group - Состав выпускающей группы.


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Реклама - Реклама и объявления.
Beatles - He's a real Nowhere Man, sitting in his Nowhere Land
Реклама - Реклама и объявления ...
Scene - History of making demo "WeeD".
News - новости от AREAsoft.

В этот день...   4 декабря