Глава 2
Организация ввода и вывода
Вообще-то говоря, чем больше мы узнаем о
программировании, тем виднее настановиться ограниченность
наших языков программирования. Языки высокого уровня, такие
как Бейсик или Паскаль, не дают всех возможностей, которые
могут понадобиться для программирования. С некоторого
момента нам нужно углубиться в систему и использовать
некоторые ее функции, либо пойти еще дальше и
программировать аппаратуру на физическом уровне.
Хотя языки высокого уровня и предусматривают
ограниченные возможности прямого обращения к памяти (POKE и
PEEK) или даже к некоторым микросхемам (IN и OUT в Бейсике),
большинство программистов в конечном счете обращаются к
языку ассемблера - основному языку, на котором строяться и
все другие языки, и даже операционные системы. Ассемблер
Z80, как и любой другой ассемблер состоит из мнемонических
инструкций. С помощью специальной программмы эти инструкциии
их аргументы транслируются в двоичных формат, называемый
машинным языком. Это именно тот формат, в котором программы
храняться в памяти и передаются через электронные схемы.
По смыслу выполняемых действий инструкции деляться на
несколько категорий. Они могут выполнять для простых
действия арифметики над 8-ми и 16-ти разрядными числами. Они
могут пересылать данные. Они могут, манипулировать
битами. Они могут проверять значения и передавать управление
в зависимости от результата. И наконец, они могут управлять
внешними схемами. Рамер инструкции переменный - от 1 до 4
байт. Наиболие часто используемые команды - самые короткие и
самые быстрые.
Программирование на ассемблере бывает двух сортов:
написание процедур, которые вы потом будете использовать в
своих программах и создание программ из этих процедур. В
любом случае, для понимания того, как Z80 обрабатывает
информацию и как взаимодействует с перефирией. В этой главе
мы рассмотрим как процессор взаимодействует с остальными
частями компьютера.
LD переслать данные
PUSH сохранить на стеке
POP востановить со стека регистровую пару
EX обменять
EXX переключиться на алтернативный регистр
LDI,LDD переслать байт
LDIR, LDDR Переслать блок данных по памяти
CPI, CPD Сравнить байт
CPIR,CPDR Поиск данных
ADD Сложить данныв
ADC Сложить, учитывая флаг переноса
SUB Вычесть данные
SBC Вычесть с учетом переноса
AND Поразрядное логическое И
OR Поразрядное логическое ИЛИ
XOR Поразрядное логическое ИСКЛЮЧАЮЩЕЕ ИЛИ
CP Сравнить операнды
DAA Преобразовать в двоично-десятичную арифметику
CPL Инвертировать биты аккумулятора
NEG Инвертировать знак аккумулятора
SCF Установить знак переноса
CCF Инвертировать знак переноса
INC Увеличить на единицу
DEC Уменьшить на единицу
RLCA Циклический сдвиг влево
RLA Циклический сдвиг влево вместе с переносом
RRCA Циклический сдвиг вправо
RRA Циклический сдвиг вправо вместе с переносом
SRL Сдвиг вправо
SRA Сдвиг вправо с переносом
SLA Сдвиг влево
RLD Перенос тетрад влево
RRD Перенос тетрад вправо
BIT Проверка значения бита
SET Установить указанный бит
RES Сбросить указанный бит
NOP Нет операций, пауза
HALT Ожидание прерываний
DI Запретить прерывания
EI Разрешить прерывания
IM Изменить режим прерываний
CALL Вызвать подпрограмму
CALL NZ Вызвать, если не ноль
CALL Z Вызвать, если ноль
CALL NC Вызвать, если не перенос
CALL C Вызвать, если перенос
CALL PO Вызвать, если нечетно
CALL PE Вызвать, если четно
CALL M Вызвать, если отрицательно
CALL P Вызвать, если положительно
RET Вернуться из подпрограммы
RET Z Вернуться, если ноль
RET NZ Вернуться, если не ноль
RET NC Выход, если не было переноса
RET C Выход, если был перенос
RET PO Вернуться, если нечетно
RET PE Вернуться, если четно
RET P Вернуться, если положительно
RET M Вернуться, если отрицательно
RETI Вернуться из прерываний
RETN Вернуться из немаскуруемых прерываний
RST Командое прерывание
JP Перейти по абсолютному адресу
JP NZ Перейти, если не ноль
JP Z Перейти, если ноль
JP NC Перейти, если небыло перенос
JP C Перейти, если был перенос
JP PE Перейти, если нечет
JP PO Перейти, если чет
JP P Перейти, если положительно
JP M Перейти, если отрицательно
JR Перейти по абсолютному адресу
JR NZ Перейти, если не ноль
JR Z Перейти, если ноль
JR NC Перейти, если небыло перенос
JR C Перейти, если был перенос
DJZN Повторять, пока в регистре В не ноль
INI,IND Принять значени из порта
INIR,INDR Принять массив из порт
OUT Послать значение в порт
IN Принать значение из порта
IN (HL),(C) Установить флаги в соотв. с счит. значением
OTIR,OTDR Послать блок данных в порт
OUTI,OUTD Послать значение
2.1 Как микропроцессор Z80 общается с внешним миром
Существует три способа взаимодействия Z80 с окружающим
миром: с помощью обращении к памяти, через порты и с помощью
сигналов прерываний.
Память используется для чтения и записи значений с
использованием адресов. К данным можно обращаться через
регистры процессора. Контроллер дисплея обращается к памяти
напрямую, миную процессор. Все остальные устройства ведут
обмен данными через регистры Z80.
Порты - универсальный способ взаимодействия Z80 с любыми
устройствами, кроме памяти. Как и ячейки памяти, порты
нумеруются, данные могут быть считаны из него или записаны в
порт с произвольным номером (адресом). Как правило, адреса
конкретных устройств для различных компьютеров отличаются,
однако семейство Спектрумов имеет стандартное распределение
портов для большинства нужд. (см табл. 2.2)
Прерывания представляют собой сигналы из внешнего мира,
сообщающие процессору о наступлении определенных событий.
Механизм вызова программ обслуживания прерываний может быть
полезен и для других целей, например в стандартном Спектруме
используются для опроса клавиш и корректировки времени.
Поскольку понимание и использование прерываний занимает
важное место в программировании компьютера, мы посвятим
прерываниям отдельный раздел в конце этой главы.
2.1.1 Форматы данных процессора Z80
Числа. Микропроцессор Z80 может использовать четыре
формата целых чисел, основанных на фунфоментальных
"строительных" блоках - 8-ми разрядном байте и 16-ти
разрядном слове. Эти единицы разрядности данных шины данных
(8) и шины адреса (16). Байт - если можно так выразиться,
более фундаментальная единица представления и при обращении
к памяти используется именно она. Один байт может хранить
целое число в диапозона от 0 до 255. Если же рассмтривать
числа со знаком, то диапозон данных, представим байтом - от
-128 до 127. Старший, седьмой бит при этом означает знак.
При необходимости работать с чиалами большего диапозона Z80
прости рассматривает два байта следующий подряд как единое
целое. Это наиблие употребимых формат для чисел -
двухбайтное целое (далее просто слово). Слово можно
рассматривать как целое со знаком в диапозоне от -32767 до
32768 (см табл. 2.3).
*******
Маленькая справка. На Z80 просто совпало, что размер
слова равен двум байтам. Обычно рамер слова принимается
равмым размерности шины адреса.
*******
Символы. Символьные данные храняться в стандартном ASCII
формате, причем каждый символ занимает один байт. Z80 ничего
не знает о ASCII и рассматривает символьные данные как
цепочки произвольных байтов и никаких действий над ними не
подразумевает.
Более подробно формат ASCII рассматривается в приложение
С.
2.1.2. Другие форматы представления фанных
Для некоторых задач необходимы другие форматы данных,
работа с которыми эмулируется с помощью множества обычных
команд. Да и сам процессор имеет форматы данных, которые до
поры до времени программисты не используют. В этой части мы
рассмотрим большинство форматов.
Ну, как вы уже наверное поняли самыми используемыми
форматами являются байт и слово, причем знак учитывается
довольно редко. Чтобы облегчить понимание дальнейшего
метериала, вы должны понимать как байт и слово ханяться в
памяти. Байт, как всем вам должно быть известно еще из
школы, состоит из битов. Бит является минимальной единицей
информации и может принимать только два значения: включено и
выключено или, говоря иначе, равен 1 и равен 0. Байт может
содержать 256 значений (два в восьмой степени = 256), от 0
до 255.
На практике мы имеем дело только с десятичными числами.
Там числа представляются степенями числа 10:
10^0 единицы
10^1 десятки и т.д.
Компьютер же представляет все степенями двойки.
Например, число 194 представляется как 11000010. Сейчас мы
рассмотрим как это число получилось.
Степени двойки равны:
0 1
1 2
2 4
3 8
4 16
5 32
6 64
7 128
11000010 = 1*2^7 + 1*2^6 + 0*2^5 + 0*2^4 + 0*2^3 + 0*2^2 +
1*2^1 + 0*2^0= 128+ 64 + 2
Надеюсь, что вам все стало ясно, но если еще кто-то из
вас блуждает в потемках не знания, то советуем обратиться к
толстым талмудам, где объясняются переводы из одной системы
счисления в другую. На первый взгляд двоичная запись очень
громоадка и неудобна, но это только на первый взгляд. На
практике, вы будете писать программы в ассмеблере, которых
понимает и двоичную, и десятичную, и шестнадцатиричную
запись. Когда это удобно вы будете использовать двоичную
запись, когда нужно десятичную и вам не будет нужно мочиться
с переводом вручную, хотя с опытом это не будет вызывать у
вас затруднений.
Могу дать один совет, легче перевести из десятичного
числа в шестнадцатиричное, а уже потом в двоичное и
наоборот. Самое время подметить, что профессионалы
используют шестнадцатиричную систему счисления. Чтобы не
утруждать себя, всегда имейте под рукой эту таблицу:
Десят Двоичн Шестн
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
Но мы отклоняемся от нашей задачи, а именно рассмотреть
форматы данных.
Фомрат, когда данные храняться в одном байте или слове,
без учета знака называется абсолютным двоичным форматом.
Но вам может понадобиться также учитывать знак, например
при операциях относительного перехода. С помощью этой
команды можно перейти на несколько шагов вперед, а можно и
вернуться назад.
Такой форамт называется двоично-дополнительным, с его
помощью можно представлять числа от -128 до 127. Младшие
семь битов хранят данные, а старший седьмой означает знак.
7 I 6 5 4 3 2 1 0
+ I
- I данные
Бит 7 равен 1 при отрицательном значение
Чтобы изменить знак необходимо инвертировать данные
(помянть нули на единицы, а единицы на нули) и прибавить 1.
В ассемблере для этого же служит команда NEG.
Следующий формат, которых поддерживает процессор - BCD
(двоичный код десятичных чисел). Каждое десятичное число
можно представить в виде четырех двоичых битов:
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
Четырехбитную группу называют полубайтом или тетрадой.
Байт состоит из двух тетрад и может содержать число от 0 до
99. Очевидно, что такой формат расточителен. Процессор имеет
три команды, работающие с BCD. Считается, что примениние BCD
может упростить вывод чисел на экран, на я с этим не
согласен.
Следующее, что мы рассмотрим, это то как хранится слово
в памяти. Слово состоит из двух байтов, старшей и младшей
части. По своему опыту мы все знаем, что сначала идут
старшие разряды, потом младшие. В компьютере все наоборот,
сначала младшие, потом старшие. Биты в памяти идут так:
7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8
Как уже ясно, процессор может представлять только целые
числа и максимальное из них равно 65535. Для большинства
целей этого достаточно, но не всегда.
Операционная система Спектрума и бейсик используют
пятибайтное представление данных. Такое предсталение
называется интегральным
Целые числа. Они принимают значения от 0 до 65535. Для
их хранения необходимо два байта. Если число положительно,
то оно хранится так:
00 00 LOW HIGH 00,
где LOW и HIGH соответственно старший и младший байт данных.
Если число отрицательно, то оно может быть в диапозоне
от -1 до -65535. Хранится оно так:
00 FF LOW HIGH 00 (FF - 255 в десятичной системе)
Действительные (вещественные) числа
Спектрум может работать с действительными числами от
-1.7014118*10^38 до +17014118*10^38. Минимальные числа
предсталяют собой -1.469367*10-39 до +1.469367*10-39
В памяти (интегральное предсталвние) данные хранятся в
экспотенциальном виде. В старшем, пятом байте, хранится
экспонента числа, увеличенная на 128. Остальные четыре байта
содержат мантиссу. Седьмой бит четвертого бита указывает на
знак мантиссы. Если он включен - число отрицательное. Те,
кто не знает что такое экспонентная форма фолжен открыть
учебник алгебры за 6 класс и прочитать все так. А мы пока
напишем как можно сжать данные и засунуть их в 4 байта.
Итак, нам необходимо записать большое число. Нам
придеться делить его на два, до тех пор пока значение по
абсолютной величине (без учета знака) не станет меньше 1, но
больше 0.5 . Сколько раз нам пришлось делить и есть
мантисса.
Если число очень маленькое, то число приходиться
умножать, а не делить. Правда экспонента станет
отрицательной. На знак экспоненты указывает седьмой бит.
Если он равен 1, то экспонента положительная.
На тот случай, если вам очень захочется вручную
представить значение в интегральной форме, придеться
пояснить, если же вам это не нужно, то лучше пропустить этот
параграф.
Допустим вам захотелось представить 1/10. Оно меньше,
чем 1/2 и нам придеться его умножать, а не делить. Когда
старший разряд перейдет за единицы, припишем его к двоичной
дроби.
0.1 0.
0.1*2=0.2 0.0
0.2*2=0.4 0.00
0.4*2=0.8 0.000
0.8*2=1.6 0.0001
0.6*2=1.2 0.00011
0.2*2=0.4 0.000110
и так далее......
Получилось 0.000110011001100110011001100... и т.д.
Сместим дробную точку вправо до первой единицы. Количество
шагов и есть экспонента, а остальное мантисса.
Экспонента равна -3, мантисса 0.110011001100... Теперь
запишем в пятом байте экспоненту+128. -3 + 128 = 125.
Запишем мантиссу в четыре байта. В старшем бите четвертого
байта поместим 0, если число положительно. В итоге мы
получили:
5 байт 4 байт 3 байт 2 байт 1 байт
01111001 01001100 11001100 11001100 11001100
Хотя мы заменили единицу в 4 байте, но необходимо
помнить, что там всегда должна быть 1, она просто
подразумевается.
Как уже видно, интегральная форма очень сложная и с ней
связан рад ошибок можете попробовать ввести PRINT INT -65536
Осталось лишь добавить что ноль представляется, как : 00
00 00 00 00
Представление строк в интегральной форме
Стринги также представляются в интегральной форме. Пяти
байт на строку конечно не хватит. Данные о строке хранятся в
интегральной форме, а сам стринг в каком-то другом месте.
00 LOW HIGH LENGHT1 LENGHT2
LOW и HIGH соответственно младший и старший адрес первого
символа строки, LENGHT1 и LENGHT2 длина строки.
2.2 Как микропроцессор Z80 работает с памятью
Поскольку у Z80 16-ти разрядная шина адреса, он не может
непосредственно работать с адресами большими чем 65535.
Теоретически это должно означать, что он может адресоваться
только к 64К памяти. Однако ранее мы утвержали, что в "128К"
используется гораздо больше. Это возможно благодаря
страничному представлению памяти.
2.2.1 Расширение памяти с помощью страниц
Z80 не может использовать более 64К памяти и даже если у
вас в компьютере микросхем на 73427К памяти, вы все равно не
сможете их использовать. Но запрограммировав контроллер
доступа к памяти, вы можете менять наборы памяти, т.е если
сейчас был один набор, то будет другой и наоборот. Доступ к
старой памяти невозможен, пока вы не переключитесь обратно.
Поясним это на примере. Например, вы маленький мальчик и
вас есть кубики и машинка. Вы не можете играть и тем и
другим одновременно, но можете играть по очереди. Играя в
кубики вы за бываете до поры, до времени о машинке.
Принцип страниц применен в режиме 128К. Там имеется 128К
ОЗУ и 32К ПЗУ. Доступ к контроллеру памяти происходит через
порт #7FFD (здесь и далее значок # означает 16-ти ричную
систему счисления, а % двоичную)
32К ПЗУ дает 2 набора операционной системы. Вы можете
менять активное ПЗУ. В любом случае оно будет начинаться с
адреса 0000 и кончается #3FFF. Для этого следует изменять
бит 4 порта #7FFD. Если там находится 1, то активно обычное
ПЗУ, если 0, то "новое" ПЗУ 128К.
С ОЗУ дело обстоит несколько сложнее. Ведь если вы
переключите память процессор продолжить выполнять программу,
но данные будут находиться уже в другой памяти. Это может
привести к сбою. Сейчас вы уведите, как этого избежали в
128К. Тут умеется, так называемая, область свободного
проецирования, начиная с адреса #C000. Все ОЗУ Спектрума
128К поделено на 8 страниц по 16К. Можно изменить значения
так, чтобы с адреса #C0000 начиналась любая из этих страниц.
Это делается изменением трех младших битов порта #7FFD. Три
бита дают нам число в диапозоне от 0 до 7 (%000-%111)
Итак, с адреса #0000 по #3FFF у нас насходиться ПЗУ, с
адреса #С000 до #FFFF страницы ОЗУ. Область с #4000 до #BFFF
не изменяется и остается всегда постоянной.
Адресам с #4000 по #7FFF всегда соответствует 5
страница, адресам с #8000 до #BFFF соответствует 2 страница.
Бит 5 включает/выключает режим 128К. Если там находится
0, то 128К, если там 1, то все расширения отключаются и
программа работает как обычных 48К компьютер.
Есть одна проблема при работе в режиме 128К. Дело в том,
что записав значение, вы не сможете прочитать его обратно.
Во избежание ошибок следует хранить копию значение
засланного в порт. Операционная система хранит значение по
адресу 23388 (#5B5C). Если вы пользуетесь стандартными
прерываниями, вам придется отправлять туда копию значения.
Нормальным значением в порту #7FFD является 0.
PROFI
В Профи примене примерно тот же принцип, что и в 128К.
Максимальный объем памяти равен 1024К. Все эта память
разделяна на 64 страницы по 16К. В Профи имеется два порта
#7FFD и #DFFD. Порт #7FFD полностью эквивалентен
применяемому в 128К, а порт #DFFD представлен в таблице 2.3
Теперь имеется два области свободного проицирования, с
адреса #С000 и с адреса #4000. Изменяется область
проецирования вместе с изменением бита 3 порта #DFFF. Если
там 0, то область начинается с #С000, если там 1, то с
адреса #4000. По умолчанию картина следующая:
#С000 Страница 7
#8000 Страница 2
#4000 Страница 5
0000 Пзу
Для определения страницы памяти в диапозоне от 0 до #3F
нам необходимо 6 битов. Три младших мы можем взять из порта
#7FFD, а остальные нам придеться "выжать" из из порта #DFFD
(биты 0-2). Страница формируется так:
Вы можете теперь отключать ПЗУ и расположить по этим ад-
ресам страницу 0. Для этого необходимо установить бит 4 пор-
та #DFFD.
Таблица 2.2 Стандартные порты ввода-вывода
Обслуживание магн, динамика, бордера #FE
Клавиатура #F7FE, #FBFE, #FDFE
#FEFE, #EFFE, #DFFE
#BFFE, #7FFE
Принтер #FB (этот порт для
всех моделей разный)
Контероллер локальной сети #F7, #EF, #E7
Kempston Джойстик #1F
Парарельный интерфейс #1F, #3F, #7F
Табл. 2.3 Форматы данных процессора Z80
Размер Знак Диапозон
десятичн. шестнадц.
8 нет от 0 до 255 от 0 до #FF
8 да - 128 до 128 #80 через 0 до #7F
16 нет 0 до 65535 от 0 до #FFFF
16 да -32768 до 32767 #8000 до #7FFF
Таблица 2.4 Значение битов порта #7FFD
D0 PAGE0 Номер актирной страницы
D1 PAGE1
D2 PAGE2
D3 SCREEN ЭКРАН 0 стандартный, 1 - находящийся в 7 стр
D4 ROM ПЗУ 0 - новое, 1 - старое
D5 LOCK Блокирует управление памятью, если устан. в 1
D6,D7 Не используются
Таблица 2.5 Назначение разрядов порта #DFFD
D0 PAGE3 Номер активной страницы (см также табл 2.4)
D1 PAGE4
D2 PAGE5
D3 SELECT Выбор окна проецирования
D4 NOROM Отключается ПЗУ и на его место устанавлива-
ется страница 0, если этот бит равен 1
D5 CP/M если равен 1, блокирует TR-DOS и включает
порты контроллера на процессорный доступ
D6 NEWSCREEN Если 1, проецирует новый экран (страница
6) в память процессора, на место страницы 2. При этом бит
D3 порта 7FFD, должен равен 1
D7 80DS Если 1, то активизируется расширенный экран
(изменяется тактовая частота резонатора)
2.2.2 24 Регистра процессора Z80
Микропроцессор Z80 спроектирован таким образом, чтобы
одновременно выполнять инструкции (например арифметичкие и
логические), принимать следующие инструкции и обращаться к
памяти. Все это делается с помощью 8-ми (16-ти) разрядных
регистров.
Всего имеется 24 специализированных регистров. 14
арифметических регистров используются для временного
хранения промежуточных результатов и операндов
арифметических и логических инструкций. Четыре 8-ми
разрядных сегментных регистра (вернее говоря, два 16-ти
разрядных) хранят адреса начала таблиц. Два регистра -
указатели, используются для обращения к памяти. Два регистра
испильзуются для специальных целей: регенерации памяти и для
указания векторов прерываний. Наконец, регистр флагов
содержит 8 флаговых битов, отражающих статус процессора или
управляющий режимами исполнения некоторых инструкций.
Арифметические регистры
Основное время при работе компьютера тратиться на
обращение к памяти. Можно сущестрвенно повысить
эффективность программ, сохраняя промежуточные результаты
вычислений и операндов внутри процессора. Для этого служат
14 арифметических инструкций, иногда называемых регистрами
данных.
Арифметичекие регистры обозначаются A, B, C, D, E, H, L,
A', B', C', D', E', H', L'. Регистры с палочкий наверху
называются альтернативными. H' читается "аш-прим". Эти
регистры являются запасными. Процессор не может работать с
альтернативными и оснывными регистрами одновременно. Для их
обмена есть особые команды (сравните с памятью 128К). Вы
можете обменять одной командой B', C', D', E', H', L' с B,
C, D, E, H, L и A' и F' на A и F другой. (о F' мы поговорим
позже подробнее). После этого основной набор становиться
альтернативным и наоборот. Процессор же считает, что ничего
не произошло. После обмена регистров НИГДЕ не указывается,
что произошол обмен, что и составляет сложность на практике.
Регистры B, C, D, E, H, L можно рассматривать как пары
регистров BC, DE, HL, что на практике и осуществляется.
Старшими регистрами (в том смысле, что в них хранить старший
байт от слова) являются H, D, B, младшими L, E, C. Регистр А
называется аккумулятором. У него нет пары и он самых важных.
Операции работающие с одинарными регистрами и регистровыми
парами могут чередоваться как угодно.
В основном эти регистры используются для временного
хранения данных, особенно операндов арифметических операций.
Сложение и вычитание регистр-регистр выполняется существенно
быстрее, чем регистр-память. Хотя эти регистры более-менее
взаимосвязаны, каждый из них имеет некоторые спациальных
функции.
BC часто используется как обратный счетчик. DE
используется как адрес назначения. HL - просто адрес или
16-ти разрядный аккумулятор. Арифметичкие регистры
используются для передачи параметров в процедуры.
Хранение слов в памяти.
Хотя адресуемой единицей памяти в компьютер авляется
байт, многие операции осуществляются на 16-ти разрядными
словами. В памяти слово храниться в двух соседних байтах.
Младший байт, образующий слово, имеет меньший адрес, а
старший байт - на единицу больший. При работе с байтами и
словами в памяти это следует четко помнить. Основной
источник ошибок здесь - способ, которым мы заполнили данные
При записи шестнадцатиричного значения мы сначала выпи-
сываем старшие цифры, а затем - младшие. Например #1234, а в
памяти храняться сначала две младшие цифры, а затем - две
старшие, т.е 3412
Индексные регистры.
Имеется четыре индексных регистра, только как пары
регистров. Это IX, IY. В них можно занести начало таблицы,
даннных, какой-то области и выбирать байты из этой таблицы,
указывая смещение. Это дает заметное удобство в работе. При
работе под управлением операционной системы регистр IY
указывает на середину специальной области системных
переменных и поэтому его менять не желательно.
Регистры-указатели
Два 16-ти ричных регистра служат для точного указания
адреса исполняемой инструкции и стека. Регистр SP указывает
на адрес стека (см далее), а регистр PC, называемый также
программным счетчиком , указывает на адрес следующей
исполняемой инструкции в памяти. Программа не может явно
изменить или получить значение последнего регистра. Для
изменения значения PC следует использовать команды JP, JR,
CALL, которые неявно влияют на PC; причем последняя команда
сохраняет значение регистра на стеке.
Регистры I и R
Эти регистры имеют очень специфичное назначение. Регистр
I, называемый регистром вектора прерываний, используется при
втором режиме прерываний и мы затронем этот вопрос, когда
будем рассматривать прерывания. Регистр регенерации памяти R
постоянно меняется от 0 до 127, т.е 7 бит его не
используется. Вы можете использовать этот бит по своему
желанию, а также весь регистр R для генереции случайных
чисел в диапозоне от 0 до 127. (старший бит 7) (или нет???)
( или все-таки старший).
Регистры флагов
Последний регистр Z80, называемый регистр флагов
содержит набор битов, называемых флагами. Флаги объединены в
8-ми разрядный регистр для того, чтобы можно минимальными
затратамии изменять флаги или сохранять их все в памяти.
Однако на практике флаговые биты используются по
отдельности.
Всего имеется 6 одновазрядных регистровых флагов, два
остальных не используются. (Хотя я встречал в литературе
иную информацию) На самом деле программист активно
использует только два из них. Логически, все эти биту -
статусные флаги отражающие результаты арифметичких и
логических операций.
Мы будем употреблять сочетание букв "установлен", когда
хотим сказать, что этот бит равен 1 и "сброшен" когда бит
равен 0.
Флаги Z80
C флаг переноса. Устанавливается, когда происходит пе-
ренос или заем в арифметичеких операциах. Один из
самых важных флагов.
N флаг отрицательного результата. Устанавливается, если
в результате арифметических операций был получен
отрицательных результат.
P/V флаг четности-переноса. В арифметических операциях
устонавливается если произошло изменение знак
(перенос). В логических операциях указывает на
четность числа установленных битов.
H флаг вспомогательного переноса. Устанавливается при
наличие переноса между тетрадами одного байта.
Указывает на необходимость коррекции при
BCD-арифметике.
Z флаг ниля. Устанавливается, если результат операции
равен равен нулю или просто равен (при сравнении).
Второй важный флаг.
S флаг знака. Устанавливается, если последней операцией
было вычитание.
Стек
Это область, используемая для временного хранения
данных. Стек - встроенная функция микропроцессора. Он
обеспечивает программам место для хранения и слежения за
процессом работы. Наиболие важное использование стека - это
запись адреса подпрограмм для нормального возвращенияя из
них. Стек также можно использовать для временного хранения
рабочих данных, хотя такое применение его менее существенно.
Стек можно сравнить с обоймой: чтобы достать самый
глубокий патрон, необходимо втащить все остальные. По умному
это называется LIFO (последним пришол - первым ушел). Это
означает, что выход в программи, чей адрес находиться на
верху стека, будет произведен первым. Таким образом,
поддерживается упорядоченная работа программ, подрограмм и
драйверов устройств вне зависимости от их сложности (однако,
если сильно постараться, то все можно нарушить).
Стек используется от основния (с наибольшего адреса) к
вершине (к наибольшему адресу), так что, когда данные
записываются в вершину стека, то они попадают в адреса
памятти, меньшие чем адрес предыдущего значения. Стек растет
и каждом помещении значения на стек SP уменьшается на 2.
Любая программа может изменить адрес стека на свой, но
это не рекомендуется делать, т.к операционная система
распологает стек в самой благоприятной области памяти.
Z80 не определяет выход за границы стека (как и сами
границы), однако если вы выделите ему на эти цели 256 байт,
то должно хватить по уши.
5B00 (FFFA)
4A00 (FFF8)
3900 (FFF6)
(FFF4)
(FFF2)
SP=FFF6 Начальное положение стека
5800 (FFFA)
4A00 (FFF8)
3900 (FFF6)
2800 (FFF4)
(FFF2)
SP=FFF4 Выполнили PUSH
5800 (FFFA)
4A00 (FFF8)
3900 (FFF6)
(FFF4)
SP=FFF6 Выполнили POP, стек вернулся в нормальное положения
Правила использования регистров
Использование регистров следует назначать по
"назначению", следуя хоть каким-то правилам. Применяя
стандартные правила, вы сможете интуитивно догадываться о
способе передачи параметров, к сожелению нельзя дать советов
на все случаи жизни, но более-менее общие я постараюсь
сформулировать.
Регистр BC следует использовать как обратный счетчик или
как указатель размера. Регистр HL используется свободно, но
чаще как "исходных адрес". DE следует использовать как адрес
назначения.
Часто для передачи параметров следует использовать стек
(стандарт Паскаля), но этот метод постепенно отживает себя.
2.3 Как микропроцессор Z80 использует порты
Для связи с различными частями компьютера и управления
ими микропроцессор Z80 использует порты ввода-вывода. Порты
- это двери через которые с компьютером происходит общение с
вспомогательными микросхемами.
Каждый порт в Z80 идентефицируется 16-ти разрядным
номером порта, который может меняться в диапозоне от 0 до
65535 (в фирменном ZX Spectrum'е дело обстоит немного иначе,
там применена не полная адресация). Процессор, задавая номер
порта посылает управляющую информацию или данные в
конкретный порт или же, наоборот, читает информацию из
порта.
Для записи и чтения в порт в Z80 существует команды OUT
и IN, аналогичные команды существуют в Бейсике. Вы можете на
бейсике поэксперементировать с портами, это никак не
повредит, максимум сотрете информацию с дискет.
2.4 Как микропроцессор Z80 реализует прерывания
Каждый раз, когда устройство или программа нуждается в
помощи микропроцессора, они посылают ему сигнал или команду.
Когда микропроцессор получает сигнал прерываний, он
останавливает всякую другую деятельность и запускает
находящуюся в памяти программу, называемую программой
обработки прерываний. После того, как программа обработки
прерываний выполнит свою задачу, работа компьютера
продолжается с того места, где она была прервана в момент
возникновений прерываний.
Так должно быть в идеале, но по некоторым причинам в
Спектруме это не совсем так. В Z80 существует три
прерываний: маскируемые (запрещаемые), немаскуруемые и
командые. С помощью маскуруемых прерываний можно создавать
парарельные процессы, т.к в Spectrume они срабатывают каждые
1/50 долю секунды.
Следует следить за состоянием стека за сохранением и
восстановлением всех регистром при написании своей процедуры
обработки прерываний.
2.4.1 Маскируемые прерывания
Как уже сообщалось эти прерывания срабатывают каждые
1/50 секунды, т.е с частотой 50 герц. В Спектруме это
используется для сканирования клавиатуры и обработки
внутренных часов.
Управление маскируемыми прерываниями происходит с
помощью команд DI и IE. Команда DI запрещает прерывания, а
EI разрешает прерывания. Эти команды влияют на тригер
прерываний. Команда DI сбрасывает этот тригер, а EI
сбрасывает его.
В Z80 имеется три режима обработки маскируемых
прерваний. Они устонавливаются командами IM0, IM1, IM2.
Процессор сохраняет внутри себя режим, но не дает
пользователю об этом узнать позже.
Режим IM0
Этот режим включается сразу после включения питания и
полностью аналогичен применяемому в микропроцессоре INTEL
8080. При каждом поступлении сигнала прерываний, устройство,
вызвавшее прерывание, должно было бы выдать на шину данные
коданды, которая должна бы исполняться. В Спектруме на шину
данных вечно выдается сигнал #FF (RST #38), поэтому режим
IM0 почти эквивалентен IM1.
Режим IM1
Этот режим стандартно включает операционная система
Спектрума. По получении сигнала прерываний процессор
передает управление на адрес #38, т.е выполняется команда
RST #38, где в Спектруме находиться Стандартная процедура
обработки прерываний.
Режим IM2
Этот режим дает возможность программисту указывать адрес
обработки прерываний.
При поступлении сигнала на шине адреса формируется
адрес. Младший адрес берется с шины адреса (а в Спектруне
это всегда #FF), а старший байт заноситься значение регистра
I. Из памяти по полученному адресу считывается значение,
которое и является адресом программы обработки прерываний.
Возврет из маскируемых прерываний следует выполнять
командой RETI
Примечание
Некоторые микросхемы и устройства могут выдавать на шину
данных значение отличное от #FF. При этом может произойти
сбой в ваших планах.
2.4.2 Немаскируемые прерывания
При поступлении сигнала немаскируемого прерывания
компьютер безусловно передает управление по адресу #66.
В Спектруме 48К с прошивкой 82 года это прерывание не
задействовано. В компюьтерах с монитором 87 года и выше при
поступлении сигнама NMI включается встроенный отладчик
памяти (дампер). При подключенном BETA-интерфейсе
срабатывает кнопка MAGIC.
Во время работы немаскируемых прерываний маскируемые
прерывания отключаются. Возврат из немаскируемых прерываний
происходит по команде RETN.
2.4.3 Тригеры прерываний
В Z80 имеется два тригера прерываний: IFF1 и IFF2. IFF1 -
это тот самый тригер, о котором мы говорили до этого. IFF2
хранит копию IFF1 на время прерываний. В таблице 2.7
представлены все возможные состояния IFF1 и IFF2.
Таблица 2.7 Тригеры IFF1 и IFF2
IFF1 IFF2
RESET 0 0 Устанавливает IM0
DI 0 0
EI 1 1
INT 0 0
RETI IFF2 ---
NMI 0 IFF1
RETN IFF2 ----
LD A,I ---- ---- IFF2->P/V
LD A,R ---- ---- IFF2->P/V
IM0 ---- ---- IM0
IM1 ---- ---- IM1
IM2 ---- ---- IM2