Переходы нужны для того, чтобы отойти от
последовательного выполнения программы и начать выполнение какого-либо блока с
другого места. В значительной степени эти команды эквивалентны командам БЕЙСИКа
GO TO.
Переходы бывают:
·
условные и безусловные;
·
относительные и абсолютные.
Условный переход выполняется (или не выполняется)
в зависимости от того, выполняется или нет какое-либо условие. Аналогичная
конструкция БЕЙСИКа выглядит так:
IF ......... THEN GO TO ...........
Безусловный переход выполняется всегда, когда он
встречается в программе. Он не связан никакими условиями.
Абсолютный переход выполняется в заданный адрес.
Адрес (двухбайтный) задается после кода операции.
Относительный переход выполняется на сколько-то
шагов вперед или назад от адреса, в котором стоит команда, следующая за
командой перехода. Здесь диапазон возможных переходов ограничен. Он может
составлять от -128 до +127 байтов и называется смещением. Однобайтная величина
смещения s задается вслед за кодом операции. Она
всегда задана в дополнительной двоичной форме.
Мнемоники команд абсолютного перехода всегда начинаются с
JP (jump - скачок), а
относительного перехода - с JR (jump relative -
скачок относительный).
Всего в этой группе 17 команд и мы рассмотрим их в
четырех подгруппах.
5.9.1. Команды абсолютного безусловного перехода.
Мнемоника
|
Код
|
Мнемоника
|
Код
|
JP
nn
|
C3
n
n
|
JP
(HL)
|
E9
|
JP
(IX)
|
DD
E9
|
JP
(IY)
|
FD
E9
|
Команда JP nn
вызывает переход к адресу, заданному двумя байтами nn.
Этот адрес автоматически загружается в программный счетчик (регистровую пару PC)
и программа продолжается, начиная с этого адреса.
Команды JP (HL), JP (IX), JP (IY)
используют косвенную адресацию. Так, например, по команде JP (HL)
выполняется переход к тому адресу, который содержится в регистровой паре HL.
5.9.2. Относительный безусловный переход.
В этой подгруппе всего одна команда: JR s, - ее код 18, за ним следует величина смещения s от -128 до +127, заданная в дополнительной двоичной форме.
Напомним, что в этой форме к примеру FE (254) означает не переход на 254 байта
вперед, а переход на 2 байта назад, т.е. (-2).
Другой особенностью относительного перехода является то,
откуда начинает отсчитываться переход вперед или назад. Запомните, он
отсчитывается не от того адреса, в котором размещалась команда JR, а от
того, в котором стоит следующий за ней код операции, т.к. когда встретилась
команда JR, в программный счетчик PC уже был заслан адрес начала
следующей команды, и от него-то и начинается отсчет. Например:
Мнемоника
|
Адрес (десят.)
|
|
Код
|
Комментарий
|
LD A,FF
|
60000
|
3E
|
Загрузить в аккумулятор число
255
|
|
60001
|
FF
|
DEC A
|
60002
|
3D
|
Уменьшить A на
1
|
JR FD
|
60003
|
18
|
Переход назад на три байта
|
|
60004
|
FD
|
NOP
|
60005
|
00
|
Пауза
|
Применение относительных переходов дает ряд ценных
преимуществ по сравнению с абсолютными как при написании, так и при
эксплуатации программ. Обратите внимание на то, что в вышеприведенном примере
нет никаких указаний на адреса. Мы его разместили начиная с адреса 60000, но
если его разместить в другом месте, он будет точно также работать, выполняя те
же действия.
Отсутствие абсолютных переходов делает подпрограмму релоцируемой
(перемещаемой). При наличии внутри нее абсолютных переходов она становится
нерелоцируемой. Правда, надо не забывать, что релоцируемая процедура может не
допускать обращений к ней из других частей программы (см. 5.12), либо перед
всяким таким обращением должно быть указано, где эта процедура находится. Может
быть выделена специальная программная переменная, которая указывает на
местоположение процедуры.
Релоцируемость удобна при написании тем, что Вы можете не
задумываться над вопросом о том, где процедура будет находиться. А при
эксплуатации Вы сможете отводить ей те области памяти, которые Вам в данный
момент удобны в зависимости от прочего окружения [прим.5].
5.9.3. Команды абсолютного условного перехода.
С помощью этих команд выполняется переход по заданному
адресу, если выполняется какое-либо условие. В системе команд Z-80 это условие
связано с состоянием флагов регистра F, а именно: флаг переноса (C),
флаг нуля (Z), флаг знака (S), и флаг переполнения/четности (P/V).
Мнемоника
|
Код
|
Комментарий
|
JP C,nn
|
DA n n
|
Переход
по заданому адресу выполняется, если флаг переноса включен.
|
JP NC,nn
|
D2 n n
|
Переход
по заданному адресу выполняется, если флаг переноса сброшен.
|
JP Z,nn
|
CA n n
|
Переход
по заданному адресу выполняется, если флаг нуля включен.
|
JR NZ,nn
|
C2 n n
|
Переход
по заданному адресу выполняется, если флаг нуля сброшен.
|
JR P,nn
|
F2 n n
|
Переход
по заданному адресу выполняется, если флаг знака включен.
|
JR M,nn
|
FA n n
|
Переход
по заданному адресу выполняется, если флаг знака сброшен.
|
JP PO,nn
|
E2 n n
|
Переход
по заданному адресу выполняется, если включено нечетное число битов или если
нет переполнения.
|
JP PE,nn
|
EA n n
|
Переход
по заданному адресу выполняется, если включено четное число битов или если
есть переполнение.
|
5.9.4. Команды относительного условного перехода.
Эти команды обеспечивают переход вперед или назад на
заданное количество байтов в зависимости от состояния флагов C и Z.
Мнемоника
|
Код
|
Комментарий
|
JR C,s
|
38 s
|
Переход
на s байтов, если флаг C включен.
|
JR NC,s
|
30 s
|
Переход
на s байтов, если флаг
C
сброшен.
|
JR Z,s
|
28 s
|
Переход
на s байтов, если флаг Z включен.
|
JR NZ,s
|
20 s
|
Переход
на s байтов, если флаг Z сброшен.
|
5.9.5. Комментарий к работе с флагами.
Флаг переноса (С). Этот флаг находится в
нулевом бите регистра F. Он показывает, было или нет переполнение
аккумулятора в абсолютной двоичной арифметике (т.е. был ли результат сложения
больше 255 или результат вычитания меньше 0). На состояние этого флага влияют
далеко не все команды. Вы можете уточнить влияние команд на различные флаги по
таблицам нашего "Справочника".
В двух словах:
1)
Все команды сложения, вычитания и сравнения ADD, ADC,
SUB, SBC, CP влияют на флаг переноса. Он включается, если
было переполнение при сложении, заем при вычитании или если при сравнении
содержимое операнда оказалось большим, чем содержимое аккумулятора.
2)
Все команды логики AND, OR, XOR сбрасывают флаг
переноса.
3)
Команды сдвига (см. разд. 5.14.) влияют на флаг переноса.
Флаг нуля (Z). Этот флаг находится в шестом
бите регистра F. Он включается, если результат предыдущей операции был
равен нулю, в противном случае он выключается.
Обратите внимание на мнемоническое противоречие. Флаг
нуля включен и не равен нулю, когда результат операции равен нулю.
Далее кратко:
1)
При работе с одиночными регистрами на флаг нуля влияют результаты
операций сложения (ADD, ADC, INC), вычитания (SUB, SBC,
DEC), сравнения (CP), а также логики (AND, OR, XOR).
2)
При работе с регистровой парой на флаг нуля влияют только арифметические
операции ADC и SBC.
3)
Команды загрузки регистров LD не влияют на флаг нуля, за
исключением очень редко встречающихся для "Спектрума" команд LD
A,I; LD A,R.
4)
На флаг нуля влияют также команды, с которыми мы познакомимся несколько
позже: команды сдвига (разд.5.14.); команды проверки битов (5.15.); команды
блочного поиска (5.16.).
Флаг знака (S). Этот флаг находится в
старшем (седьмом) бите регистра F. Он включается, если результат
отрицательный и сбрасывается, если положительный.
Интересно, что для операций, выполняемых в дополнительной
двоичной арифметике, он равен старшему (седьмому) биту аккумулятора, который
тоже определяет знак содержимого.
Основные особенности:
1)
При работе с одиночными регистрами на флаг знака влияют результаты
операций сложения (ADD, ADC, INC), вычитания (SUB, SBC, DEC),
сравнения (CP), а также логики (AND, OR, XOR).
2)
При работе с регистровой парой на флаг знака влияют только операции ADC
и SBC.
3)
Команды загрузки регистров LD не влияют на флаг знака, за
исключением команд LD A,I; LD A,R.
4)
На флаг знака влияют также команды, с которыми мы познакомимся несколько
позже: команды сдвига (разд.5.14.); команды блочного поиска (5.15.).
Флаг переполнения/четности (P/V). Этот флаг находится
во втором бите регистра F. Он имеет двойное назначение. Для одних команд он
указывает на наличие четности числа включенных битов, для других - на наличие в
результате операции переполнения в дополнительной двоичной арифметике.
Пояснение: Понятие четности относится здесь не к
числу, находящемуся в аккумуляторе, а к количеству его включенных битов.
Например, число 33 (0011 0011) имеет четыре включенных бита, и, следовательно,
флаг включен, а у числа 34 (0011 0100) - три включенных бита и флаг выключен.
Понятие «переполнение» здесь относится к дополнительной
двоичной арифметике. Флаг включается, если в результате операции сложения
возникает переход от числа, лежавшего в диапазоне 00…7F к числу из диапазона 80…FF
или при вычитании - наоборот. Одним словом, он включается, если по правилам
дополнительной двоичной арифметики возникает смена знака содержимого
аккумулятора, неважно в какую сторону.
Точно установить какие операции влияют на этот флаг как
на флаг четности, какие как на флаг переполнения, а какие вообще не влияют, Вы
можете по таблицам нашего «Справочника…».
Очень часто в программах бывает нужным повторить
какой-либо блок вычислений n раз. В БЕЙСИКе для
этих целей служит оператор цикла:
FOR i = 1 TO n
..............
NEXT i
Все, что находится между операторами FOR и NEXT будет
повторено n раз. При этом параметр i,
называемый параметром цикла, будет с каждым проходом увеличиваться на единицу
и, когда достигнет n, выполнение цикла будет
прервано и работа программы продолжится с оператора, следующего за NEXT.
В машинных кодах для этой цели служит мощная команда DJNZ
s. Эта команда относится к регистру B
процессора. Когда процессор встречает эту команду, он уменьшает на единицу
содержимое регистра B, проверяет его на ноль, и, если ноль достигнут, выполняет
переход на s байтов. Величина смещения s задана в двоичной дополнительной форме и переход может
быть как вперед, так и назад, но чаще, конечно, назад. Вычисления повторяются
до тех пор, пока не будет достигнут 0 в регистре B.
Мнемоника - DJNZ s;
Код - 10 s.
Поясним это на примере.
Вы уже знаете, что когда процессор встречает команду NOP,
он ничего не делает, т.е. просто выдерживается пауза. Продолжительность ее - 4
тактовых цикла. Длительность такта зависит от частоты задающего кварца Вашего
компьютера и может несколько меняться от машины к машине. Но, поскольку она
примерно 3,5МГц, то продолжительность одного цикла - доли микросекунды.
Предположим, что Вы хотите, чтобы процессор выдержал паузу примерно 200
тактовых циклов. Вы можете, конечно, поместить подряд 50 команд NOP, но
это очень грубо и, к тому же, наносит большой ущерб количеству свободной
оперативной памяти.
Воспользуемся возможностью организации цикла вычислений и
многократным повторением команды NOP. По таблицам «Справочника…» можно
найти, что время выполнения команды DJNZ занимает 13 тактовых циклов,
если в регистре S не 0 и 8 циклов, если 0.
Всего, чтобы пауза длилась примерно 200 тактов, надо
повторить команду NOP 12 раз:
12*4 + 11*13 + 1*8 = 48 + 143 + 8 = 199
Тогда процедура будет выглядеть так:
Мнемоника
|
Код
|
Комментарий
|
LD B,12
|
06
|
Загрузка
в регистр B числа 12
|
|
0C
|
0C HEX = 12 DEC
|
NOP
|
00
|
Пауза
|
DJNZ 253
|
10
|
Уменьшить
содержимое регистра B на единицу
|
|
FD
|
и
переход назад на 3 байта.
|
Эти команды позволяют программисту копировать содержимое
регистровых пар на стек и, наоборот, вызывать их оттуда в процессор. Таким
образом, за один прием пересылаются сразу два байта, т.е. двухбайтное число
(как правило, это адрес, но не всегда). Кроме того, в этой же группе есть
команды, которые позволяют производить обмен содержимого некоторых регистров
процессора.
Рассмотрим в этой группе три подгруппы команд.
5.11.1. Команды сохранения данных на стеке.
Эти команды начинаются с мнемоники PUSH, которой
подходит несколько несерьезный русский эквивалент «затолкнуть» (в дальний
ящик). Итак, они применяются, когда надо временно сохранить до дальнейшей
потребности содержимое регистровых пар.
Мнемоника
|
Код
|
Мнемоника
|
Код
|
PUSH
AF
|
F5
|
PUSH
HL
|
E5
|
PUSH BC
|
C5
|
PUSH DE
|
D5
|
PUSH IX
|
DD
E5
|
PUSH IY
|
FD
E5
|
Сначала на стек переносится старший байт, а затем
младший. После операции PUSH указатель стека (регистр SP) уменьшается на
две единицы. Уменьшается потому, что стек «растет» сверху вниз. Мы об этом уже
говорили.
5.11.2. Команды вызова данных со стека.
По этим командам производится снятие данных со стека и
загрузка их в необходимый регистр. Следует подчеркнуть, что данные, находящиеся
на стеке, ни к какому регистру не привязаны. Независимо от того, из какого
регистра они были выгружены, загружать их можно в любой другой регистр и вообще
нет никакой физической возможности узнать из какого регистра эти данные
выгружались. Если это важно, программист сам должен за этим следить.
Мнемоника команд, вызывающих данные со стека, начинается
со слова POP («вытолкнуть»).
Мнемоника
|
Код
|
Мнемоника
|
Код
|
POP AF
|
F1
|
POP HL
|
E1
|
POP BC
|
C1
|
POP DE
|
D1
|
POP IX
|
DD
E1
|
POP IY
|
FD
E1
|
5.11.3. Команды обмена
со стеком.
Этих команд три. Они начинаются с мнемоник EX (exchange - обменивать) и служат для того, чтобы отправить на
стек содержимое регистровой пары, а содержимое вершины стека отправить в эту
регистровую пару. То же самое можно сделать, использовав еще один регистр в
качестве временного места хранения, но применением команд обмена это делается
проще.
Мнемоника
|
Код
|
EX
(SP),HL
|
E3
|
EX (SP),IX
|
DD
E3
|
EX (SP),IY
|
FD
E3
|
5.11.4. Замечания к операциям со стеком.
Стек процессора - машинный стек. Его не следует путать со
стеком GO SUB, который организуется при работе в БЕЙСИКе и служит для
запоминания номера строки, из которой вызывалась подпрограмма, Его также не
следует путать со стеком калькулятора. «Спектрум» имеет калькулятор, который
управляется процедурами, размещенными в ПЗУ, и может использоваться при
программировании в машинных кодах. С ним мы познакомимся довольно глубоко в
разделе 5.13.
Машинный стек служит не только для того, чтобы
обеспечивать программисту удобное место для временного хранения данных. Его
основное назначение - хранить адреса, из которых вызываются подпрограммы. Это
нужно для того, чтобы по завершении подпрограммы процессор знал, куда ему надо
вернуться для продолжения вычислений. В связи с этим, при работе со стеком, от
Вас требуется определенная внимательность.
Если Вы находитесь в подпрограмме, то все, что Вы
поместите на стек, должно быть снято оттуда до того, как произойдет выход из
подпрограммы в вызывающую программу. С другой стороны, поскольку выход
выполняется по адресу, находящемуся в двух байтах вершины стека, Вы можете
искусно управлять логикой работы программы, манипулируя числами, находящимися
на вершине стека, в частности, так организуются сложные вычисления в
программах.
В БЕЙСИКе подпрограммы вызывались с помощью оператора GO
SUB, а возврат после исполнения подпрограммы выполнялся командой RETURN.
В машинных кодах им эквивалентны команды CALL – «вызов»
и RET – «возврат».
5.12.1. Команды вызова.
Вызов может быть безусловным или сопровождаться
каким-либо условием, связанным с проверкой состояния флагов регистра F.
Всего имеется 9 команд для вызова подпрограмм.
Мнемоника
|
Код
|
Комментарий
|
CALL nn
|
CD n n
|
Это
безусловный вызов процедуры, находящейся по адресу nn.
|
CALL C,nn
|
DC n n
|
Вызов
подпрограммы, если флаг переноса включен.
|
CALL NC,nn
|
D4 n n
|
Вызов подпрограммы, если флаг
переноса выключен.
|
CALL Z,nn
|
CC n n
|
Вызов
подпрограммы, если флаг нуля включен.
|
CALL NZ,nn
|
C4 n n
|
Вызов
подпрограммы, если флаг нуля выключен.
|
CALL N,nn
|
FC n n
|
Вызов
подпрограммы, если флаг знака включен.
|
CALL P,nn
|
F4 n n
|
Вызов
подпрограммы, если флаг знака выключен.
|
CALL PE,nn
|
EC n n
|
Вызов
подпрограммы, если флаг переполнения/четности включен.
|
CALL PO,nn
|
E4 n n
|
Вызов
подпрограммы, если флаг переполнения/четности выключен.
|
5.12.2. Возврат из подпрограммы.
Этих команд тоже 9. Они начинаются с мнемоники RET.
Когда процессор встречает такую команду, он снимает два верхних числа с
машинного стека и отправляет их в программный счетчик (регистр PC).
Имейте в виду, что восстановленный адрес из машинного стека может быть и не
тем, который был туда отправлен по команде CALL. Вы ведь могли его изменить,
как намеренно, так и ненамеренно.
Мнемоника
|
Код
|
Комментарий
|
RET
|
C9
|
Безусловный
возврат в вызывающую программу.
|
RET C
|
D8
|
Возврат,
если флаг переноса включен.
|
RET NC
|
D0
|
Возврат, если флаг C
выключен.
|
RET Z
|
C8
|
Возврат,
если флаг нуля включен.
|
RET NZ
|
C0
|
Возврат,
если флаг нуля выключен.
|
RET M
|
F8
|
Возврат,
если флаг знака включен.
|
RET P
|
F0
|
Возврат,
если флаг знака выключен.
|
RET PE
|
E8
|
Возврат,
если флаг переполнения/четности включен.
|
RET PO
|
E0
|
Возврат,
если флаг переполнения/четности выключен.
|