Как ориентироваться в машинном языке 1969 г.

Ветвление - команды перехода, цикла. Подпрограммы.


         В   общем   случае  ЦП  выполняет  команду  перехода
следующим  образом:  если флаг нуля установлен, прибавить 2 к
счетчику команд (это составит 32005). Если флаг нуля сброшен,
не  нужно  делать  ничего  (счетчик  команд  остается  равным
32003).  Иными словами, относительный переход позволяет нам в
определенных  случаях перескакивать через команду LD B,0. Это
также  объясняет, почему для этой команды указаны два времени
выполнения.  Подсчет нового значения счетчика команд занимает
больше времени, чем невыполнение никаких действий. Поэтому ЦП
выполнит либо команду, расположенную по адресу 32003, либо по
адресу 32005, в зависимости от значения флага нуля. 
         Как   мы   уже   говорили,   можно   также   сделать
относительный переход с отрицательным значением. 

                         УПРАЖНЕНИЕ.

         Поскольку относительный переход - 2-байтовая команда
и  счетчик  команд после относительного перехода указывает на
следующую   команду,   к  какому  результату  приведет  такая
команда 

                           JP -2 ?

            ЦИКЛЫ "FOR.....NEXT" НА МАШИННОМ ЯЗЫКЕ

         Вы,  я  уверен,  знакомы с формой цикла "FOR...NEXT"
языка "Бейсик": 

                        FOR I = 1 TO 6
                        LET C = C+1
                        NEXT I
         Эквивалентное  этому  предложению  на машинном языке
сходно  по  смыслу,  но  приобретает  несколько  иную  форму.
Давайте  рассмотрим,  как  мы  могли  бы  реализовать цикл на
машинном   языке   с   помощью   арифметических   функций   и
относительных переходов: 

         LD B,1  : SET COUNTER TO 1- установить счетчик =1 
         LD A,7  : MAX. OF COUNTER + 1- максимальное значение
LOOP     INC C   : C = C+1 счетчика 
         INC B   : INCREMENT COUNTER- увеличение счетчика
          CP B   : IS B = A?  - равно ли?
       JP NZ,LOOP: IF NOT LOOP AGAIN -если нет,повторить цикл

         Такая  программа  будет работать, но нужно заметить,
что  мы связываем пары регистров - одну для увеличения и одну
для  хранения  максимума  - и команда наращивания счетчика по
завершении  не  знает  никаких  флагов. Гораздо лучше было бы
уменьшать  значение  счетчика. Мы знаем, что должны выполнить
цикл  6  раз,  так почему не задать B = 6 и не уменьшать его?
Тогда у нас получится такая программа: 

       LD B,6       : SET COUNTER - установить счетчик
LOOP  INC C         : C + C+1
      DEC B         : DECREASE COUNTER - уменьшить счетчик
       JP NZ,LOOP   : LOOP IS NOT FINISHED - цикл не закончен

         Вы  можете заметить, что такой способ эффективнее. У
чипа  Z80 есть специальная команда, соединяющая последние две
приведенные выше строки. Эта команда записывается так: 

                           DJNZ   Д

и  читается:  "уменьшить  (B)  и  перейти, если не нуль". Это
2-байтовая  команда,  и поэтому она дает экономию в один байт
по сравнению с приведенным выше текстом. 
         Из-за  наличия  этой  специальной  команды регистр B
обычно  используется для реализации счетчиков. Ограничение на
команду  DJNZ состоит в том, что можно считать только до 256.
Тем  не  менее, команды DJNZ могут быть вложенными, если есть
такая необходимость: 

        LD   B,10H   :B = 16
BIGLOOP PUSH BC      :SAVE VALUE OF "B" - запомнить 
                     :значение в B
        LD   B,0     :SET B = 256       - задать
                     :LITLOOP 
           ..........
                     :WHATEVER CALCULATION-произвольные 
                     :вычисления
           ..........
        DJNZ LITLOOP :DONE 256 TIMES? -  выполнился 256 раз?
        POP  BC      :GET BACK VALUE OF 8-получить вновь 
                     : значение B
        DJNZ         :DO BIGLOOP 16 TIMES-выполнить  
                     :охватывающий  цикл  16  раз  

                          УПРАЖНЕНИЕ

         Запишите  на  кусочке бумаги, что будет находиться в
каждом регистре после выполнения каждой команды в приведенной
выше программе. 

                       ЦИКЛЫ  ОЖИДАНИЯ

         В  программах  на  машинном  языке  бывают ситуации,
когда  события  происходят  с  такой  большой  скоростью, что
необходимо ввести некоторое ожидание. 
         Примеры,  сразу приходящие на ум, посылка информации
на  кассету  магнитофона  (сигналы  должны  быть  расположены
достаточно  далеко  друг  от друга, чтобы их потом можно было
прочитать)  или  посылка  информации  на  печатающую  машинку
(представьте себе печать со скоростью нескольких тысяч знаков
в секунду). 
         Поэтому  полезно установить циклы ожидания с помощью
команды DJNZ: 

                               LD B,COUNT
                       WAIT  DJNZ WAIT
             COUNT - счетчик
             WAIT - ожидание

         Команда DJNZ WAIT заставит ЦП возвратиться к команде
DJNZ  столько  раз,  сколько  нужно,  чтобы  регистр  B вновь
обнулился, прежде чем обработка пойдет дальше. Это должно вам
дать  ответ  на  ваше упражнение, в котором спрашивалось, что
произойдет, если вы напишете: 

                        WAIT  JR  WAIT

         Вам  придется довольно долго ждать пока ЦП выйдет из
этого цикла. 

               КОМАНДЫ ГРУППЫ ВЫЗОВА И ВОЗВРАТА


                      TIME       EFFECT  ON  FLAGS
   MNEMONIC     BYTES      ЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄ
                      TAKEN
                             C    Z   PV    S    N    H


 CALL ADDRESS     3    17    -    -    -    -    -    -
 CALL CC,ADDRESS  3   10/17  -    -    -    -    -    -
 RET              1    10    -    -    -    -    -    -
 RET CC           1    5/11  -    -    -    -    -    -



Замечание:   CC  -  условие,которое  должно  выполняться  для
мсполнения команды. Ниже приводятся используемые условия: 

           флаг   сокращение              значение
    ______________________________________________________
        перенос      C        установка флага переноса (=1)
                    NC        сброс флага переноса  (=0)
         нуль        Z        установка флага нуля (=1)
                    NZ        сброс флага нуля (=0)
       четность     PE        флаг четности четный (=1)
                    PO        флаг четности нечетный (=0)
         знак        M        знак минус (=1)
                     P        Знак положительный (=0)

Воздействие  на  флаги:  обратите  внимание,  что  ни один из
флагов не изменяется при командах вызова и возврата. 
Время выполнения: если указаны два значения времени, то более
короткое относится к случаю, когда условие не выполняется 

    ПРИМЕНЕНИЕ ПОДПРОГРАММ В ВАШИХ ПРОГРАММАХ НА МАШИННОМ
                            ЯЗЫКЕ

         Применение  подпрограмм  в  машинном  языке столь же
просто,  как  в обычных программах на языке "Бейсик", если не
проще.  На самом деле, вы помните, что применяя функцию USR в
своей  программе  на  языке  "Бейсик",  вы в действительности
вызываете подпрограмму, и, как вы помните, для завершения нам
нужна  команда  RETURN.  Поэтому  вам  очень просто проверить
определенные   подпрограммы   независимо  от  вашей  основной
программы на машинном языке. Основное различие, с которым вам
придется  столкнуться  при  реализации  подпрограмм  в  ваших
программах  на  машинном  языке, состоит в том, что вам нужно
знать адрес начала подпрограммы. Это может вызвать трудности,
если  вы  храните  программы  на  машинном языке в переменном
массиве,   поскольку   адрес  этой  переменной  необязательно
фиксирован.  Это  также  означает,  что программа на машинном
языке  не может быть легко перемещена в новую позицию памяти,
если  в  ней  используются  подпрограммы.  Подпрограммы можно
также  вызывать  в  зависимости от условий. Это эквивалент на
машинном языке следующего предложения на языке "Бейсик": 

               IF (CONDITION) THEN GOSUB (LINE)

Находясь  в подпрограмме, нужно проявлять осторожность, чтобы
не  изменить  какие-либо  флаги или регистры, требующиеся для
дальнейших  сравнений.  Это  необходимо для того, чтобы вы не
ущли  на ветвь алгоритма снова по следующему предложению CALL
после возврата туда, откуда вышли. 
         Различие    состоит   в   том,   что   единственными
допустимыми условиями являются состояния четырех флагов: 
         - флаг переноса; 
         - флаг нуля;
         - флаг четности ( а также флаг переполнения); 
         - флаг знака.
         Напомним,   что  все  эти  флаги  устанавливаются  в
соответствии   с   последней  командой,  повлиявшей  на  этот
конкретный   флаг.  Поэтому  хороший  стиль  программирования
предполагает    помещение    команды    CALL    или    RETURN
непосредственно    после   команды,   устанавливающей   флаг.
Например, 

                        LD A,(NUMBER)
                        CP 1
                      CALL Z,ONE
                        CP 2
                      CALL Z,TWO
                        CP 3
                      CALL Z,THREE

         Приведенная  выше программа позволяет вам переходить
к различным программам в зависимости от значения, хранимого в
ячейке  NUMBER.  Но  обратите внимание, что она предполагает,
что   подпрограммы   не   изменяют  значение  в  регистре  A.
(Почему?) 
         Можно применить и более короткую программу, если вам
известно,  что для хранимого в NUMBER значения имеются только
три заданные выше возможности: 

                        LD A,(NUMBER)
                        CP 2
                      CALL Z,TWO     : A = 2
                      CALL C,ONE     : A (2 =) A = 1
                      CALL THREE     : A (2 =) A = 3

         Так    происходит   потому,   что   команда   CP   2
устанавливает  и флаг нуля, и флаг переноса. А команда вызова
не влияет на какие бы то ни было флаги. 
         Аналогичным   образом,  очень  полезным  оказывается
условный возврат из подпрограммы. Но это не считается хорошим
стилем программирования. 

        КОМАНДЫ ГРУППЫ СРАВНЕНИЯ И ПЕРЕМЕЩЕНИЯ БЛОКОВ


                    TIME            EFFECT  ON  FLAGS
   MNEMONIC   BYTES       ЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄЇЇЇЇЇЄ
                    TAKEN
                            C    Z   PV    S    N    H


   LDI          2    16     -    -    #    -    0    0
   LDD          2    16     -    -    #    -    0    0


   LDIR         2   21/16   -    -    0    -    0    0
   LDDR         2   21/16   -    -    0    -    0    0


   CPI          2    16     -    #    #    #    1    #
   CPD          2    16     -    #    #    #    1    #


   CPIR         2   21/16   -    #    #    #    1    #
   CPDR         2   21/16   -    #    #    #    1    #


Время  выполнения:  для  команд  повторения  указанное  время
относится  к каждому циклу. Более короткое значение относится
к  прерыванию команды. Например, для CPIR либо BC =(HL), либо
A = (HL). 

                    ОПЕРАЦИИ  НАД  БЛОКАМИ

         К настоящему моменту вы должны быть вполне знакомы с
языком,  на  котором  говорит  ваша  ЭВМ. Это очень похоже на
изучение  иностранного  языка:  вы  понимаете,  что  овладели
языком,   когда   можете   думать   на   нем.  В  этой  главе
рассматривается  последний  набор  очень полезных программ. В
нескольких   последующих   главах   мы  будем  иметь  дело  с
командами,  которые  приятно  иметь  под  рукой и которые при
определенных  обстоятельствах играют свою роль, но в общем вы
должны  уметь  писать  машинные программы на машинном языке с
помощью   уже   известного   вам  материала.  Тем  не  менее,
обязательно  прочтите главу о планировании вашей программы на
машинном языке. Рассматриваемые в этой главе команды по самой
своей   природе   способны  одним  махом  охватывать  длинные
конструкции  быстрее  летящей  пули,  иными словами, команды,
обрабатывающие  целые  блоки памяти, а не отдельные 8-битовые
байты. 
         Давайте начнем с простейшей из них: 

                             CPI

         При   вашем   знании  языка  Z80,  вы  должны  сразу
распознать  одного из представителей семейства "сравнений". И
на самом деле это и есть расширенное сравнение. По русски она
читается  "сравнить  и  дать  приращениме".  Вы  помните, что
сравнивать  что  бы  то  ни  было  можно  только с содержимым
регистра  A  и  об  этом  в  команде  не требуется упоминать.
Команда  CPI  сравнивает A с (HL) и автоматически увеличивает
HL.  Это означает, что после операции CPI HL уже указывает на
следующую  ячейку  и  готов  к  повторению ее. С помощью этой
команды  мы могли бы написать программу просмотра всей памяти
в поисках конкретного совпадения следующим образом: 

              SEARCH    CPI
                         JR N2,SEARCH
  SEARCH - поиск

         При   таком   способе,   пока  не  будет  обнаружено
совпадение (будет установлен флаг нуля, как во всех операциях
сравнения)  программа будет продолжать просмотр. К сожалению,
это  не  такая  хорошая  идея,  поскольку, если совпадение не
будет  обнаружено, программа никогда не закончится. К счастью
конструкторы  языка  Z80  позаботились  об этом и команда CPI
также автоматически уменьшает BC. Поэтому мы можем по желанию
выбирать  длину  блока, который мы хотим просмотреть, и таким
образом задать конец просмотра. Давайте предположим,что длина
просматриваемого  нами блока не превышает 255 байтов, так что
счетчик  BC  будет  храниться  только  в регистре C. Тогда мы
могли бы написать: 

                    SEARCH    CPI
                               JR Z,FOUND
                              INC C
                              DEC C
                               JR NZ,SEARCH
                    NOT FOUND ..............
                              ..............
                        FOUND...............

SEARCH -поиск;  FOUND - найдено;  NOT FOUND - не найдено 
         Очевидно,  если бы длина блока превышала 255 байтов,
нужна   была   бы  другая  программа.  Обратите  внимание  на
применение  команд  INC и DEC для проверки условия C = 0. Эти
две   команды  требуют  по  одному  только  байту  каждая,  и
поскольку  обе  они  действуют  на  флаг  нуля,  результат их
действия  состоит в том, что флаг устанавливается только если
C был первоначально равен нулю. Еще одно преимущество состоит
в  том,  что  при  таком  написании  программы  не изменяются
никакие другие регистры. 
         Теперь  мы могли бы также захотеть просмотреть блоки
памяти начиная с вершины, а не с конца. Поэтому у нас имеется
команда: 

                             CPD,

которая   по   русски   читается   "сравнить   и  уменьшить".
Уменьшение,  конечно, относится к HL, а результат для нас тот
же  самый.  Еще  большими возможностями, чем эти две команды,
обладают следующие истинные титаны в мире команд: 

                             CPIR
                             CPDR

         Они читаются: "сравнить, увеличить и повторить"  и
                       "сравнить, уменьшить и повторить". 
         Эти   двухбайтовые   команды  обладают  невероятными
возможностями:  они  позволяют  ЦП  автоматически  продолжить
просмотр  блока памяти до тех пор, пока либо не будет найдено
совпадение, либо не будет достигнут конец блока. Естественно,
нам  нужно  указать A,HL и BC перед началом, но даже при этих
условиях это - невероятно экономичный способ записи. 
         Поскольку  завершение работы этой команды происходит
в  двух возможных случаях (а именно, при найденном совпадении
в  середине  блока  и  при отсутствии совпадения вообще), нам
необходимо  обеспечить  наличие  некоторого  текста  в конце,
чтобы  проводить  различие  между  этими двумя возможностями.
Вам,  однако,  следует знать, что вне зависимости от скорости
выполнения   программ   на   машинном  языке  CPIR  и  другие
аналогичные  команды  могут  отнимать  очень  много  времени.
Команда  CPIR, например, затрачивает 21 цикл на поиск каждого
байта. Конечно, каждую секунду выполняется 3500000 циклов, но
даже  с  учетом  этого  поиск  среди 3500 байтов требует 1/50
секунды.   Это  может  показаться  вам  не  очень  длительным
временем,  но  если учесть, что выдача изображения на дисплей
осуществляется   каждые  1/50  секунды  или  около  того,  вы
поймете, что это может оказаться важным. 
         Остальные  блочные  операции  строятся  вокруг  идеи
перемещения информации. Вот они: 

                          LDI   LDIR
                          LDD   LDDR

         Очевидно,  они  относятся  к  семейству "загрузок" и
читаются как: 
         - загрузить и дать приращение;
         - загрузить, дать приращение и повторить;
         - загрузить и уменьшить;
         - загрузить, уменьшить и повторить.
         Разберем  сначала самую простую из них. LDI - это на
самом деле комбинация следующего набора действий: 
         - загрузить (HL) в (DE),
         - дать приращение DE,HL,
         - уменьшить BC.
         Обратите  внимание,  что  это  единственная команда,
загружающая из одной ячейки памяти в другую без необходимости
загрузки сначала в регистр. 
         Применение  регистра DE в качестве адреса-цели очень
разумно.  Так  вы  никогда  не  забудете,  в  каком  регистре
содержится адрес-цель. 
         Симметричная этой команде LDD совершенно совпадает с
этой,  за  исключением  того, что в процессе загрузки HL и DE
уменьшаются.  Разница между LDI и LDD оказывается важна в том
случае,  когда  два  блока  (тот, где информация находится, и
тот,  в  который она направляется) пересекаются. Предположим,
мы  применяем  эту  команду  в  прикладной  задаче  текстовой
обработки и хотим удалить слово из предложения: 

            THE BIG BROWN DOG JUMPED OVER THE FOX

           1 3 5 7 9 1 3 5 7 9 1 3 5 7 9 1 3 5 7 9

Если  мы  хотим  удалить  слово  BROWN,  то  нам нужно только
сдвинуть остальную часть предложения влево на 6 литер. 

              DE = цель     = литера 9
              HL = источник = литера т15
              BC = счетчик  = 24 литеры

Давайте  начнем  с  LDI.  После  первой  команды  у нас будет
первоначально  
                     = THE BIG BROWN DOG JUMPED OVER THE FOX
 Сдвиг на 1 литеру             D<----D
 Новое предложение   = THE BIG BROWN DOG JUMPED OVER THE FOX
 В HL = 10, DE = 16, BC = 23.
 После двух следующих команд:
                       THE BIG DOGWN DOG JUMPED OVER THE FOX
 и после того, как все команды выполнены
                       THE BIG DOG JUMPED OVER THE FOX. E FOX.
Если  бы  мы хотели, чтобы часть предложения после точки была
заменена пробелами, то этого можно было бы достигнуть за счет
добавления  пробелов  в  конце  первоначального предложения и
увеличения BC, скажем, до 30. 
         Если  мы  теперь  решили  обратить  этот  процесс  и
возвратить  слово  BROWN  в  предложение, то нам нужно просто
снова  применить команду LDI, поскольку мы забьем информацию,
которую мы хотим сдвинуть. 

 Например,    HL = источник      = литера 9
              DE = цель          = литера 15
              BC = счетчик       = 24 литеры

После выполнения одной команды у нас будет:
первоначально       = THE BIG DOG JUMPED OVER THE FOX.E FOX.
сдвиг на литеру               D---->D
новое предложение   = THE BIG DOG JUMPED OVER THE FOX.E FOX.
после выполнения 6 команд мы получим
                      THE BIG DOG JUDOG JUVER THE FOX.E FOX.
пока  все  хорошо.  Но  после  выполнения  следующих 3 команд
получим: 
                      THE BIG DOG JUDOG JUD OG THE FOX.E FOX.

Трудность состоит в том, что мы забили информацию, которую мы
хотели   перемещать.   Вы   можете   проверить  это,  пытаясь
переместить по одному символу вручную. 
         Поэтому  лучше  использовать команду LDI с регистром
DE,  указывающим  на  конец предложения. Это гарантирует, что
информация  не  будет  забита при перемещении. Команды LDIR и
LDDR  обладают  еще  большими возможностями, они могут быстро
перемещать тысячи байтов. 

                         УПРАЖНЕНИЕ.

         Напишите   короткую  программу  для  перемещения  32
байтов из части памяти, занятой ПЗУ, на экран. 
         Обратите  внимание, как организованы первые 32 байта
экрана. Затем попробуйте переместить 256 байтов, а затем 2048
байтов. 

           БОЛЕЕ  РЕДКО  ИСПОЛЬЗУЕМЫЕ  КОМАНДЫ  Z80

                       ОБМЕН РЕГИСТРАМИ

         Мы  коротко  рассмотрели  в первых нескольких главах
идею  ЦП, имеющего набор перчаток, которые он может снимать и
надевать,  и  таким образом хранить информацию в таком месте,
которое  более  доступно, чем ячейка памяти. Вы, должно быть,
помните,    что    вы    не   можете   манипулировать   этими
альтернативными  регистрами,  и  аналогия с перчатками в этом
плане  очень  ценна. Хотя они и сохраняют свою форму, сами по
себе  они  не  могут  выполнять  арифметические  действия или
счет. 
         Первая команда такова: 

                          EX AF,AF'

         Она  делает  в  точности  то,  что  подсказывает  ее
название:  "обменять  пары  регистров  AF  и  AF'". Используя
аналогию  с  перчатками, мы сказали бы: "переодеть перчатку с
пары  рук  AF". Иными словами, надеть запасной набор перчаток
AF - вы помните, что запасной набор всегда обозначается AF'. 
         Следующая общая команда обмена перчаток такова: 

                             EXX

         Эта  команда  переодевает перчатки на всех остальных
8-битовых регистрах следующим образом: 

                      B C         B' C'
                      D E   (-)   D' E'
                      H L         H' L'

         Поэтому  это  очень  мощная  команда,  но  именно ее
возможности   делают   ее   ограниченной  в  применении.  Так
происходит потому, что она действует на все регистры сразу, и
невозможно оставить ни одно значение (за исключением регистра
A, на который EXX не действует). 
         Единственный   способ  преодолеть  эту  трудность  -
написать короткую программу такого типа: 

                           PUSH HL
                           EXX
                           POP HL

         Это  означает, что вы запомнили значения BC, DE и HL
в  наборе  альтернативных регистров, но по-прежнему оставляем
для работы HL. 
         Последняя  команда  из  этой группы на самом деле не
относится к типу "переодевания перчаток": 

                           EX DE,HL

         В   этой   команде   задается   перекрестный   обмен
содержимым  между  регистрами  HL  и DE. Эта команда на самом
деле   очень   полезна,   поскольку,  как  мы  видели,  HL  -
привилегированная   пара   регистров   во  многих  прикладных
задачах,  и  есть  ситуации,  когда  нужное  нам значение для
обработки находится в DE.



СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



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

Похожие статьи:
Чёртова дюжина неудобных вопросов членам жюри - Андрей Лазарчук.
Вечный думатель - Любовь к заводным апельсинам.
CPU для Вас - Работа с TR-DOS на уровне машинных кодов.
Розыск - Разыскиваются: CYRUS-2 128 & FINAL FIGHT.
Под знаком "Пи" - О музыкалке First Association.

В этот день...   21 ноября