3. МАШИННЫЙ ЯЗЫК Z80 Регистры Z80A
Во время выполнения программы компьютер не обновляет непосредственно содержимое памяти. Он копирует содержимое ячейки памяти в регистр и оперирует содержимым регистра. Регистры в машинном языке имеют функцию, схожую с переменными в BASICe, т.е. используются для хранения чисел. Они отличаются от переменных BASlCa тем, что число их ограничено и они существуют в процессоре, а не в RAM. Кроме того, они могут хранить только один байт, или 2 байта в паре регистров.
Z80A - имеет несколько регистров и потому может хранить несколько чисел одновременно. Благодаря этому снижается время при обмене информацией между процессором и памятью.
Регистр "А"(Аккумулятор)
Аккумулятор - это наиболее важный регистр, т.к. больше всего арифметических и логических команд выполняется с содержимым этого регистра. Этот регистр называется аккумулятором, т.к. результат последовательных операций накапливается ("аккумулируется") в нем. Некоторые команды, которые обращаются к аккумулятору, используют другой регистр или адрес памяти в качестве источника данных. Например, инструкция
ADD А,В
дает процессору команду прибавить содержимое регистра B к содержимому регистра A, поместив результат в A._
Флаг |
Мнемоника |
Использование |
Знак |
N |
P |
Включается, когда результат последней операции отрицательный. |
Нуль |
Z |
NZ |
Включается, когда результат последней операции равен нулю или имело место совпадение. |
Перенос |
C |
NC |
Включается, когда происходит переполнение регистра, т. е. последний результат больше того, что может быть записанным в один байт (или в два байта для операций с парой регистров). |
Четность/ Переполнение |
РЕ |
РО |
Флаг включается, если в байте результата предыдущей операции количество включенных битов есть величина четная. В некоторых операциях этот флаг свидетельствует о переполнении. |
Таблица 3. 2. Четыре флага, которые контролируют наибольшее количество операции Z80A. |
Флаг-регистр "F" (регистр состояний)
Регистр F довольно значительно отличается от всех остальных, т.к. его содержимое не рассматривают как один байт, а рассматривают как 8 индивидуальных битов, что конечно же одно и то же. Эти биты используются в качестве так называемых флагов для управления последовательностью выполнения программы. Каждый флаг используется для определения того, какое из двух логически противоположных условий имело место при выполнении предыдущей операции. Например, флаг нуля определяет, был ли равен нулю результат последней операции сложения, вычитания и т. п. Только 4 из восьми флагов наиболее интересны для пользователей, их свойства кратко изложены в Таблице 3.2.
Флаг знака наиболее простой. Кроме того, условленно, что в операциях со знаковыми числами седьмой бит (старший бит в байте) тоже используется для хранения знака. Он включается, когда число отрицательно, иначе снимается.
Этот флаг отражает знак последнего результата.
Флаг нуля устанавливается, если результат последней операции равен нулю. Он также используется инструкциями сравнения, которые фактически аналогичны вычитанию, при котором результат игнорируется.
Флаг переноса регистрирует переполнение, которое имеет место, если результат сложения длиннее чем то, что может быть записано в регистр, либо имеет место заем при вычитании. Имеется также несколько инструкций сдвига, в которых биты в регистре A сдвигаются влево или вправо циклически через флаг переноса.
Флаг четности/переполнения - это два флага в одном. Он используется, как флаг переполнения при выполнении арифметических операций для определения, был ли бит 7 получен в результате переноса или сгенерирован битом 6 при "взятии займа". Это используется в случае, если бит знака был искажен. Логические инструкции используют тот же самый флаг для определения четности результата.
Примечание: четность двоичного числа - это четность количества битов, установленных в единицу. Если количество четно, то говорят, что имеет место четность, если оно нечетно, то говорят, что имеет место нечетность. Флаг устанавливается, если имеет место четность.
Результат некоторых инструкций зависит от текущих установок отдельных флагов, например, инструкция JR Z, D
позволяет Z80A "перепрыгнуть" через несколько инструкций, если флаг нуля установлен (т. е. сделать условный переход типа IF...THEN GO ТО). Если флаг нуля не установлен, процессор выполняет следующую инструкцию в обычной последовательности, т.о. флаговый регистр очень важен, т.к. он позволяет процессору принимать решения и переходить к другой части программы.
Регистры счетчики "B"и "C"
Регистр B и, в некоторой степени, регистр C, с которым он может использоваться в паре, доступен для нескольких применений. Но наиболее важно использование его в качестве счетчика. Мы уже видели, как выполнение программы может управляться использованием флага 0 в инструкции JR z, n.
Другая инструкция: DJNZ n использует флаг 0 для создания циклов, используя регистр B в качестве счетчика аналогично циклам FOR-NEXT в BASICe. Когда встречается эта инструкция, Z80A уменьшает содержимое регистра B на 1. Если результат равен нулю, то выполняется следующая инструкция в нормальной последовательности, если результат не равен нулю, подпрограмма "перескакивает" n инструкций. Если программист использует отрицательное значение для n, "прыжок" осуществляется в программе назад и, если здесь нет других переходов, процессор, конце концов, встретит ту же самую инструкцию вновь.
Т.о. загрузкой регистра B определенным числом и установкой соответствующего смещения n определенная часть кодов может быть выполнена заданное количество раз. Регистр B содержит только один байт и поэтому может устанавливаться на любое число от 0 до 255, т.е. используя описанный механизм через определенный блок кодов может быть сделано максимум 255 проходов. Для осуществления более 255 проходов в цикле нет эквивалентных инструкций, но имеются очень мощные инструкции, которые используют все 16 битов регистровой пары ВС, как счетчик с максимальным значением 65535. Например, инструкция CPDR. Когда Z80A выполняет ее, то:
1) Содержимое ВС уменьшается на 1;
2) Уменьшается содержимое HL (о регистровой паре HL см. ниже);
3) Сравнивается содержимое аккумулятора A с содержимым ячейки памяти, адрес которой находится в паре регистров HL.
Процессор повторяет эти действия до тех пор, пока не будет совпадения между содержимым A и ячейки памяти, либо пока не обнулится BC, т.о. эта инструкция может быть использована для поиска ячейки, содержащей определенное число.
Адресные регистры "DE"и "HL"
Регистры D и Е не имеют особых функций и в основном используются в качестве временной быстро доступной памяти, они также используются в паре для хранения адреса ячейки, которая интересует нас в текущий момент.
Основная функция регистров H и L - хранить в паре адрес ячейки памяти. Мы уже видели, как определенная инструкция использует пару HL с этой целью. H хранит старший байт, L - младший. Полный адрес доступен в форме:
адрес = 256* Н + L,
что дает максимум 65536 возможных адресов.
Индексные регистры "IX" и "IY"
Индексные регистры IX и IY являются 16-битовыми регистрами и могут быть использованы только так в отличие от регистров ВС, DE и HL, которые могут использоваться и в паре, как 16-битовые регистры, и индивидуально, как 8-битовые. IX и rY, главным образом, используются подобно паре HL.
IX и IY, кроме того, имеют одно свойство, которое недоступно для HL - они могут быть использованы со смещением S. Имеется в виду, что инструкция, которая ссылается к (IX+S), использует не ячейку памяти, адрес которой хранится в IX, а сначала смещение S прибавляется к значению в IX, чтобы получить новый адрес, и с ним и работает инструкция.
Указатель стека "SP"
Стек - это область в верхней части ОЗУ, которая используется для временного хранения содержимого пар регистров. Стек растет вниз, когда идет заполнение и поднимается вверх при его опустошении. Начало стека фиксировано в ZX SPECTRUM - оно находится непосредственно ниже ячейки, на которую указывает системная переменная RAMTOP. Вершина стека находится ниже нижней границы стека, т.к. стек увеличивается вниз, а уменьшается вверх. Адрес текущей ячейки верхней границы стека хранится в SP регистре. Передача в стек или из стека происходит с помощью инструкций PUSH и POP. Например:
PUSH HL
В этом случае процессор:
1) Уменьшает SP;
2) Копирует содержимое регистра H в ячейку, указанную указателем стека SP;
3) Уменьшает SP;
4) Копирует содержимое регистра L в ячейку, указанную указателем стека SP.
Инструкция POP выполняет противоположное действие. По этому методу последняя
поступившая пара чисел, помещенная в стек, является всегда первой парой, которая снимается со стека. Это обеспечивает простой и удобный способ временного хранения содержимого регистров во время вызова подпрограмм.
Программный счетчик "PC"
Программный счетчик PC очень важный 16-битовый регистр, т.к. он хранит адрес следующей инструкции, которая должна быть выполнена. Нормальный ход работы когда инструкция выполняется следующим образом:
1) копировать содержимое ячейки, указанной программным счетчиком PC в специальный регистр процессора.
2) Если инструкция содержится в нескольких байтах, увеличить PC и скопировать содержимое следующей ячейки во второй специальный регистр.
3) Увеличить PC таким образом, чтобы он указывал на следующую инструкцию, которая должна быть выполнена.
4) Выполнить инструкцию, которая только что была прочитана. Команда JUMP так же, как и DJNZ n или JR z,n , изменяет нормальный ход выполнения программы с помощью изменения PC во время выполнения шага 4. Заметим, что это изменение имеет место после увеличения PC. Таким образом, значение смещения n всегда будет отсчитываться относительно позиции инструкции, следующей за инструкцией, содержащей смещение.
Альтернативные регистры AF',BC',DE',HL'
Z80A имеет копию каждого из A, B, C, D, Е, Н и L регистров. Отличаются копии использованием знака " ' " возле обозначения регистра. Например, А' - это копия регистра
А. Инструкции не работают с этими регистрами непосредственно, но инструкции обмена позволяют вывести из использования 2 или более регистров и ввести в использование вместо них их копии.
Команды обмена выполняются очень быстро, т.к. содержимое регистра физически не копируется из одного регистра в другой, а вместо этого установка внутренних коммуникаций процессора изменяется так, что дублирующий регистр становится основным, а оригинальный регистр запасным.
О системе команд процессора Z80
В системе команд Z80 около 700 инструкций. Поскольку возможны только 256 различных комбинаций из 8 битов (2Л8=256), то лишь менее половины команд может быть выражено одним байтом. Оставшиеся инструкции хранятся в двух или трех байтах. Некоторые команды следуют с однобайтовым смещением S, или однобайтовым числом n, или двухбайтовым числом (адресом) nn, к которым инструкция ссылается. Всего же одна
инструкция может занять максимум 4 байта.
* * *
ПРИМЕЧАНИЕ "ИНФОРКОМа"
ZX SPECTRUM располагает еще встроенным программируемым калькулятором, имеющим свою систему команд. Доступ к ним возможен только из машинного кода. Команды калькулятора могут быть более длинными, чем команды процессора и занимать более 4-х байт. (См. т. 1 нашего трехтомника).
Для удобства работы на ассемблере все команды записываются с помощью мнемоник (OP CODE). Мнемоника - это сокращенное описание каждой команды.
Правила задания команд в ассемблере:
1. Одиночные регистры описываются буквой, например - В. Пара регистров именуется в алфавитном порядке, например, BC.
2. Смещение S положительно, если оно находится в пределах от 0 до 127, и отрицательно, если оно находится в пределах от 128 до 255. Большие или меньшие числа недопустимы.
Отрицательное значение подсчитывается вычитанием S из 256. Например, команда относительного безусловного перехода:
JR S
вызывает переход вперед на 8 байтов, если S=8 и переход назад на 8 байтов, если S=248, т.к.
256-8=248.
Запомните при подсчете смещения, что переход осуществляется с адреса первого байта следующей команды.
3. Однобайтовое число n лежит в пределах от 0 до 255 включительно.
4. Двухбайтовое число или адрес представляется как nn и лежит в пределах от 0 до 65535 включительно.
5. nn, заключенное в скобки, т.е. (nn), подразумевает "содержимое ячейки по адресу nn", тогда как nn без скобок подразумевает "число nn".
Т.о.,
LD HL,(23627)
подразумевает загрузку пары регистров HL содержимым ячеек 23627 и 23628, тогда
как:
LD HL,23627
подразумевает загрузку HL числом 23627.
Таким же образом, (HL) подразумевает "содержимое ячейки с адресом, хранящимся в HL", тогда как HL без скобок означает "число, хранящееся в HL".
Такой вид адресации называется косвенной адресацией.
6. Место назначения результата операции всегда задается первым, например:
ADD A,B
означает "прибавить содержимое регистра B к содержимому регистра A и результат оставить в регистре A".
РАЗДЕЛ B
4. ВВЕДЕНИЕ
В Разделе B представлены программы в машинном коде. Для простоты и удобства использования они даны в стандартном формате. Во введении описывается этот формат и дается программа на BASICe, которая может быть использована для загрузки программ в память.
Это длина программы в байтах.
Выполнение некоторых программ может потребовать переменных: изменения значения одной или более переменных, передаваемых в программу через буфер принтера.
Каждая программа дана в виде последовательности целых положительных чисел, помещаемых в последовательные ячейки памяти. Контрольная сумма (т. е. сумма всех чисел вводимой подпрограммы) дается для того, чтобы Вы были уверены в правильности загрузки
Дается краткое описание задачи, выполняемой с помощью программы.
Определяются имя, длина и адрес в буфере принтера каждой переменной. Переменная длиной в один байт (целое положительное число в пределах от 0 до 255) передается в программу из BASICa или с клавиатуры через РОКЕ.
POKE ячейка, значение
Двухбайтовая переменная передается с помощью 2-х команд:
POKE ячейка, значение - 256*1МТ(значение/256): POKE ячейка+1,INT(значение/256)
Используемые ячейки являются ячейками памяти буфера принтера
Вызов Программы вызываются с использованием функции USR, которая
подпрограммы: должна быть включена в команду. Если программа в машинном коде
не передает какое-то значение обратно в BASIC по завершению, то используется команда:
RANDOMIZE USR адрес
Если Вам надо, чтобы результат был возвращен в регистровой паре ВС, то вызов делается так:
LET A = USR адрес или
PRINT USR адрес
в зависимости от того, должны ли возвращаемые данные сохраняться в переменной BASICa или выводиться на экран._
Контроль ошибок: Объясняются проверки, выполняемые программой для нелогичных
или противоречивых значений переменных, параметров и т. п.
Комментарии: Объясняются возможные варианты в программах.
Листинг в машинном Программы представлены на языке ассемблера. Для загрузки в
коде: память используется третья колонка - "числа для ввода". Все числа
здесь даны в десятеричной системе. Как она работает: Объяснение принципа работы программы.
Загрузчик машинного кода
Почти все программы из этой книги перемещаемые, т.е. они будут работать корректно независимо от того, в каком месте RAM мы их поместим. Если программа не перемещаемая, то в комментариях объясняется, как она должна быть изменена, если ее нужно сохранить в другой области памяти. В Разделе A (часть 2) мы видели, что SPECTRUM использует различные части RAM для различных функций и что область между ячейками, указанными системными переменными RAMTOP и UDG предназначена для хранения подпрограмм в машинном коде.
Программа ВР может быть использована для загрузки, изменения и перемещения программы в машинном коде. С ее помощью пользователь может переустановить указатель RAMTOP, что даст больше свободного пространства для машинных кодов; ввести программу с клавиатуры; перейти вперед или назад для корректировки ошибки; вставить или удалить часть программы.
Когда программа ВР запускается, она печатает младший адрес, с которого программа в машинных кодах может быть введена и сохранена, т.е. на единицу больший, чем RAMTOP.
В машине с 48К памяти младший адрес - 65368, если пользователь не обновлял системную переменную RAMTOP. В конце ОЗУ обычно резервируются 168 байтов для UDG, но программа позволяет пользователю использовать и эту область, если он пожелает. Он может также выбрать новый возможный младший адрес, который программа затем помещает в системную переменную RAMTOP, используя команду CLEAR. Данные не могут быть введены по адресу, меньшему, чем 27000, т.к. иначе нарушатся границы области, требующейся для самой программы ВР. Программа ВР запрашивает адрес, с которого должна стартовать программа в машинных кодах. Т.о. пользователь может резервировать область для нескольких процедур и затем загружать их каждую отдельно.
Рисунок BF1 показывает формат дисплея после того, как с ячейки 32000 была загружена программа "screen invert". Первая колонка - адрес, вторая - содержимое ячейки памяти с этим адресом, третья - контрольная сумма. Программа "screen invert" - имеет 18 байтов в длину и ее контрольная сумма = 1613. Следовательно, она занимает ячейки от 32000 до 32017, и контрольная сумма дана для ячейки 32017, т. е. сумма содержимого ячеек (32000...32017) равна 1613.
На основном экране внимание пользователя привлекается к одной ячейке -содержимое этой ячейки мерцает. Эта ячейка является текущей и первоначально это выбранный стартовый адрес программы. Пользователь вводит целое число между 0 и 255 включительно, которое программа МС LOADER помещает в текущую ячейку. Затем следующий адрес становится текущей ячейкой. Пользователь может не вводить число, а вместо этого выбрать для корректировки вариант, описанный в таблице BT1.
Код |
Вариант |
В |
Перейти на один адрес назад. |
В п(число) |
Перейти на n адресов назад. |
F |
Перейти на один адрес вперед. |
F п(число) |
Перейти на n адресов вперед. |
I n( число) |
Вставить n байтов, каждый из которых содержит 0. |
D п(число) |
Удалить n байтов в текущей области. |
T |
Закончить программу |
Таблица BT1. Возможные варианты редактирования машинного кода |
Программа ВР. загрузчик машинного кода. (МС LOADER)
100 GO SUB 8100
200 REM ***** Вычисление доступной памяти 210 LET min= 1+РЕЕК 23730+256*РЕЕК 23731 220 LET P = РЕЕК 23732+ 256*РЕЕК 23733 230 LET t = Р - min + 1 400 REM ***** Определение стартового адреса
410 PRINT "Lowest possible start - ";min,,,"Maximum space availbie = ";t 420 INPUT "Do you wish to change the lowest start address (Y or N) ?";z$ 430 IF Z$="Y" OR Z$="y" THEN GO TO 7000
440 INPUT "Enter address at which to start loading machine code";a 450 IF a<min OR a>p THEN BEEP .2,24: GO TO 440 500 GO SUB 8100 510 LET t=t-a+min
520 PRINT "You can use up to ";t;" bytes",,, 530 LET U=PEEK 23675+256*PEEK 23676
540 IF a<u AND u<p THEN PRINT "If you use more than ";u-a;" bytes, you will overwrite the
user defined graphics area. " 550 IF a>=U THEN PRINT "You will overwrite the user defined graphics area." 560 INPUT "Is that OK (Y or N) ? ";z$ 570 IF Z$="N" OR z$="n" THEN GO TO 7000 580 IF Z$<>"Y" AND z$<>"y" THEN BEEP .2,24: GO TO 560 700 REM *** GO AHEAD AND LOAD 710 LET i=a 750 GO SUB 8200
760 INPUT "Enter number, b, f, i, d or t "; z$ 770 IF z$="" THEN BEEP .2,24: GO TO 760 780 LET a$=CHR$(CODE Z$(i) - 32*(Z$(i)>"@"))
790 GO TO 800+200*(a$="B")+300*(a$="F")+400*(a$="I")+500*(a$="D")+600*(a$="T") 800 LET X=VAL Z$
810 IF i>p THEN BEEP .2,24: GO TO 750
820 IF X<0 OR X>256 OR X<>INT X THEN BEEP .2,24: GO TO 760
830 POKE i,X
840 LET i=i+1
850 GO TO 740
1000 REM *** Перемещение вперед 1010 LET i=i-1
1020 IF LEN Z$>1 THEN LET i=i+1-VAL Z$(2 TO) 1030 IF i<a THEN LET i=a 1040 GO TO 740
1100 REM *** Перемещение назад 1110 LET i=i+1
1120 IF LEN Z$>1 THEN LET i=i-1+VAL Z$(2 TO)
1130 IF i>p THEN LET i=p
1140 GO TO 740
1200 REM *** Вставка
1210 IF LEN Z$=1 THEN LET n=1: GO TO 1225
1220 LET n = VAL Z$(2 TO): IF n<1 OR n>p-1 OR n<>INT n THEN BEEP .2,24: GO TO 740
1225 CLS: GO SUB 8100: PRINT TAB 6; "Inserting in Progress"
1230 FOR J=p TO i+n STEP -1
1240 POKE J,PEEK (j-n)
1250 NEXT J
1260 FOR J=1 TO i+n-1
1270 POKE J,0
1280 NEXT J
1290 GO TO 740
1300 REM *** Удаление
1310 IF LEN z$=1 THEN LET n=1: GO TO 1330
1320 LET n=VAL z$(2 TO): IF n<l OR n>P-1 OR n<>INT n THEN BEEP .2,24: GO TO 74O
1330 IF n<0 OR n>p-1 THEN BEEP .2,24: GO TO 1320
1340 CLS: GO SUB 8100 :PRINT TAB 6; "DELETING IN PROGRESS"
1350 FOR J=1 TO p-n
1360 POKE J,PEEK (j+n)
1370 NEXT J
1380 GO TO 740
1400 STOP
1401 PRINT AT 21,7;"PROGRAM TERMINATED" 1410 STOP
7000 REM *** Изменение RAMTOP
7010 INPUT "ENTER NEW START ADDRESS ";a
7020 IF a<27000 OR a>p THEN BEEP .2,24: GO TO 710
7030 CLEAR a-1
7040 RUN
7999 STOP
8100 CLS
8110 PRINT TAB 6; "MACHINE CODE LOADER",,, 8120 RETURN
8200 REM *** Вывод содержимого памяти 8210 GO SUB 8100
8220 PRINT "ADDRESS DECIMAL CHECK SUM" 8230 LET c=0
8240 LET s=i-8 : IF s<a THEN LET s=a : GO TO 8280 8250 FOR j=a TO s-i 8260 LET c=c+PEEK j 8270 NEXT j
8280 LET f=s+17 : IF f>p THEN LET f=p 8290 FOR j=s TO f 8300 LET c=c+PEEK j
8310 PRINT AT j-s+3,i;j: TAB 12:PEEK j; TAB 22;c
8320 NEXT j
8400 LET pos=i-s+3
8410 PRINT AT pos,12; FLASH 1; PEEK i 8420 RETURN
MACHINE CODE LOADER
ADDRESS |
DECIMAL |
CHECK SUM |
32000 |
33 |
33 |
32001 |
0 |
33 |
32002 |
64 |
97 |
32003 |
1 |
98 |
32004 |
0 |
98 |
32005 |
24 |
122 |
32006 |
22 |
144 |
32007 |
255 |
399 |
32008 |
122 |
521 |
32009 |
150 |
671 |
32010 |
119 |
790 |
32011 |
35 |
825 |
32012 |
11 |
8З6 |
32013 |
120 |
956 |
32014 |
177 |
1133 |
32015 |
32 |
1165 |
32016 |
247 |
1412 |
32017 |
201 |
1613 |
Рис. BP1.
Представлен экран во время работы загрузчика машинного кода - программа "Screen Invert" загружена с адреса 32000.