| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
СИП и СИА, а по фронтам сигналов ВВОД и ВЫВОД — в которые превратились два бита,
указывавшие тип шинного цикла. В результате на совмещенной шине данных-адреса
нету временных интервалов, когда информацию на неё выставляют одновременно два
устройства. Это позволяет при необходимости сделать её на элементах "с тремя
состояниями" а не "с открытым коллектором", что для одноплатных систем, где
длина проводников — сантиметры, значительно удобнее.
А в цикле чтение-пауза-запись сигнал СИП там выдаётся два раза — при выдаче
слова данных и при получении. Вот и возникли у меня смутные сомнения:
действительно ли я пересказал существующий протокол а не изобрёл по ходу
пересказа свой собственный?
В качестве примера типичного внешнего устройства рассмотрим последовательный
интерфейс, к коему подключался уже упоминавшийся консольный терминал. Прочие
подобные устройства (все они по классификации UNIX`а — "байтовые", т.е типа Ц)
такие-же в точности.
На самом деле это два устройства: устройство ввода, к которому подключена
клавиатура, и устройство вывода — к нему подключен буквопечатающий механизм.
Как именно подключен — не важно. Ну скажу, что две последовательные линии связи
типа "токовая петля" — в ту и в другую сторону со старт-стопным протоколом
(коий я уже описывал в главе 15). То есть аналог писишного ком-порта, но с
гальванической развязкой и дальностью связи не пара метров а раз в сто больше.
Подключение может быть и параллельное (как для принтера) — со стороны машины
всё это выглядит совершенно одинаково.
Каждое из устройств — два регистра: регистр состояния и регистр данных.
В регистре состояния задействованы всего два бита: старший ("знаковый") бит
байта по адресу 177560 — признак готовности, указывающий, что в регистре данных
— байте по адресу 177562 — код очередной нажатой клавиши. При его считывании
признак готовности автоматически сбрасывается. Следующий после него бит -
разрешение прерывания. Если он установлен, то при установке признака готовности
устройство начинает требовать прерывание по вектору 60. В регистре состояния
могут быть задействованы и другие биты. Чаще всего присутствует признак
переполнения — старший (опять же знаковый) бит всего слова по адресу 177560. Он
устанавливается когда пришел следующий байт, а предыдущий еще не считан.
Устройство вывода устроено в точности так же, но работает в обратную
сторону: там признак готовности (в младшем байте по адресу 177564)
устанавливается когда оно готово принять очередной байт, и автоматически
сбрасывается при записи чего либо в регистр данных по адресу 177566. Вектор
прерывания у него 64.
Разновсякие параметры последовательного интерфейса (скорость, количество
стоповых бит, наличие контроля четности), а так же адрес и вектор -
устанавливаются перемычками на плате устройства.
Интерфейс принтера или чего ни будь типа перфоратора не отличаются
решительно ничем (кроме адреса и номера вектора). А интерфейс перфосчитывателя
имеет еще один бит в регистре состояния (самый младший) — при записи в него
единички побуждающий перфосчитыватель считать очередной байт. Т.к. в отличии от
сидящего за клавиатурой человека, сам он инициативу проявить не может.
Признак готовности (да и ошибки) не просто так размещен в старшем "знаковом"
разряде — так его гораздо проще проверять ("на минус"). А бит "активации" -
тоже не зря в самом младшем: записать в этот разряд единичку не изменяя всех
остальных — элементарно: с помощью команды inc, читающей содержимое ячейки,
прибавляющей к нему единичку и тут-же записывающей обратно. (Читается из этого
младшего бита, разумеется, всегда ноль.)
Вот мы наконец и добрались до центрального процессора (ЦП). "Центральный" он
потому, что возможны еще и "периферийные" процессоры. Например процессор (он же
"канал") ввода/вывода. Но разумеется не в этой архитектуре.
ЦП архитектуры PDP-11 — типичная "регистровая" машина: имеет восемь
совершенно одинаковых регистров общего назначения (РОН), обозначаемых обычно как
R0..R7. И еще так называемое "слово состояния программы" (ну или процессора -
ССП), содержащее признаки. Этот набор признаков плюс содержимое РОН — полностью
определяет состояние выполняющейся в данный момент программы. Если их
аккуратненько где ни будь сохранить, а потом через некоторое время восстановить
на место — программа "ничего не заметит" и будет спокойно выполняться дальше.
(Если конешно не попортить используемые ею области памяти.)
ССП в минимальном варианте содержит четыре признака результата предыдущей
операции (NZVC — вот именно в таком порядке), один бит разрешения прерывания, и
еще один признак Т, используемый при отладке. А в максимальном (для старших
моделей) варианте бит разрешения прерывания превращается в три — уровень
"приоритета" выполняемой в данный момент программы, и добавляются два комплекта
по два бита — коды привилегированности текущего и предыдущего режимов работы.
Приоритет — это номер линии запроса прерывания, запросы с которой уже не
рассматриваются. (Правда линий по-максимуму четыре, а приоритетов — восемь.)
А режим работы это фактически номер задействованного в данный момент диспетчера
памяти, каковых у самой старшей модели за каким-то контрабасом три штуки. (А
еще у неё раздельные адресные пространства для программы и данных. Правда на
слове состояния процессора это никак не отображается.) Но впрочем никаких
(столь характерных, например для писишки) "исторических наслоений" (ни в ССП,
ни где либо еще) — следов того, как разработчики тужатся втиснуть в конструкцию
нечто, заранее не предусмотренное. Что в свою очередь — результат ранее
принятых сиюминутных решений; нежелания (а то и неспособности) думать на
перспективу. Впрочем и никакой дурной избыточности — результата борьбы
бюрократическими средствами с предыдущим явлением (по принципу: "вали кулём -
потом разберём!"), расплачиваться за которую приходится, разумеется, потребителю.
Все битики красиво и четко распределены заранее — еще в момент закладки всего
модельного ряда.
И регистры и ССП и слова, которыми процессор обменивается по шине — все
шестнадцатиразрядные. Адресуется каждый байт, но за раз передаётся всё равно
целое слово. По-этому для данной архитектуры важно чтобы информация была
выровнена по словам. Байт-то читается и пишется по любому адресу, а при
обращении к целому слову младший бит адреса просто игнорируется.
Все регистры — совершенно равноправные, но два из них дополнительно
используются для специальных целей: R7 — как счетчик команд, а R6 — как
указатель аппаратного стэка. Тоесть при выборке из памяти очередной команды,
адрес для этого берется из R7, после чего его содержимое увеличивается на два.
Адрес этот обязательно должен быть четный (иначе ошибка), потому как команда
тоже целиком занимает двухбайтовое слово. Адрес в регистре R6 используется
аппаратурой в двух ситуациях: там (на аппаратном стэке) при прерывании
сохраняется самая необходимая информация о состоянии процессора — два слова:
содержимое регистров R7 и ССП; и одно слово — при переходе к подпрограмме. Адрес
в регистре R6 при этом автоматически уменьшается (на 4 и на 2 соответственно) -
в этой архитектуре стэк растёт в сторону младших адресов. Запись на стэк и
чтение с него тоже производится исключительно целыми словами. (Младший бит R6,
если он вдруг единичка, просто игнорируется.) Но в остальном эти два регистра
решительно ничем не отличаются от всех остальных.
PDP-11 машина двухадресная в том смысле, что результат выполнения команды
помещается на место второго операнда. Или единственного — одноадресные команды
там разумеется тоже есть. (В расширенном наборе есть так-же полутора-адресные
команды: кодов на всё что надо не хватило и один операнд получился
"сокращенный" — только номер регистра.)
В отличии от машин первого и второго поколения с их машинными словами
немеряной длины и при том довольно коротким адресом (с оперативной памятью в
те времена были большие проблемы), позволяющим запихнуть их в машинное слово
несколько штук — здесь такой фокус при всём желании не получится: размер адреса
равен размеру команды. Поэтому вся адресация производится через регистры. Каждый
операнд занимает шесть бит: три бита — номер регистра, и еще три — "метод
адресации" указывающий что с этим регистром делать. Этих самых методов адресации
— восемь: четыре "основных" и четыре "более косвенных": где операнд предыдущего
используется как адрес.
0 (000) — регистровый — обозначается Rx, где x — номер регистра
1 (001) — косвенно регистровый — обозначается @Rx или (Rx) — можно так и так
2 (010) — инкрементный — обозначается (Rx)+, в регистре адрес, который ПОСЛЕ
его употребления увеличивается: на единицу — если обращение к байту или на два
если обращение к слову. Но R6 и R7 — всегда на два.
3 (011) — косвенно-инкрементный — обозначается @(Rx)+
4 (100) — декрементный — обозначается -(Rx), уменьшение адреса в регистре
производится ПЕРЕД его использованием для получения операнда
5 (101) — косвенно-декрементный — обозначается @-(Rx)
6 (110) — индексный — обозначается Е(Rx), где Е — вот этот самый индекс,
занимающий следующее слово после команды. Индекс складывается с содержимым
регистра (который при этом никак не изменяется) и обращение происходит по
полученному таким методом адресу. Типа: индекс — адрес начала массива, а в
регистре — смещение его элемента, к которому надо произвести обращение.
7 (111) — косвенно-индексный — обозначается @Е(Rx)
Таким образом двухадресная команда на самом деле может занимать до трёх слов.
Так как в этой машине всё по восемь — естественной для неё является
восьмеричная система счисления. Шестнадцатиразрядное слово делится по три бита,
каждые из которых превращаются в одну восьмеричную цифру. И вся система команд
организована так, чтобы поля командного слова приходились по границам
восьмеричных цифр — ну на сколько это вообще возможно. В частности самый старший
"знаковый" бит, оставшийся в гордом одиночестве, по-возможности используется как
однобитное поле, например указывающее размер операнда.
Итак, слово — 16 бит; команда занимает его целиком. Каждый "полноформатный"
операнд это номер регистра (одна восьмеричная цифра, т.е. три бита) и метод
адресации, указывающий что с этим регистром делать. Итого 6 бит. Для двух
операндов надо 12. Еще один бит (самый старший, как было сказано выше) указывает
размер операнда. В результате на код операции остаётся одна восьмеричная цифра
— 3 бита. Тоесть двухадресных полноформатных команд может быть только восемь.
А если учесть что нужны так же и другие команды (в частности для управления
порядком выполнения действий) и под них надо бы тоже зарезервировать парочку
кодов (0 и 7) — маловато получается. Но ничего не поделаешь — машинное слов не
резиновое. Выглядит это так (одна буква — восьмеричная циферка):
rКaabb где К — код операции, r — размер операнда: aa и bb — операнды A и B.
1 (001) — MOV A,B — пересылка A -> B или B=A
2 (010) — CMP A,B — сравнение (A-B)?
3 (011) — BIT A,B — сравнение побитовое (A&B)?
4 (100) — BIS A,B — установка битов (операция ИЛИ) A|B -> B или B|=A
5 (101) — BIC A,B — сброс битов (операция И-НЕ) ~A&B -> B или B&=~A
а вот здесь кодов не хватило — пришлось занять под код операции бит r
06 (0110) — ADD A,B — сложение (только для слов) A+B -> B или B+=A
16 (1110) — SUB A,B — вычитание ( -//— ) B-A -> B или B-=A
Здесь A и B — операнды такого вида, как описано выше. То есть в кодах каждый
из них — две восьмеричные цифры, код операции — еще одна, и последняя самая
старшая, на которую пришелся всего один бит — размер операнда (0 — слово, 1 -
байт). Для команд сложения и вычитания этот бит — часть кода операции (увы),
а сами эти команды — только с целыми словами. Впринципе (с учетом одноадресных
команд) этот набор функционально-полный. Но без команды исключающее-ИЛИ всё-же
как-то кисло...
Примеры:
MOV R2,R5; код 010205 — пересылка содержимого регистра R2 в регистр R5
SUB R2,R5; 160205 — вычитание этих-же регистров, т.е. R5 -= R2;
BIC R2,R5; 050205 — сброс битов по маске — R5 &= ~R2;
BIS @R2,R5; 041205 — побитовое ИЛИ — R5 |= *R2;
BIS R2,(R5); 040215 — *R5 |= R2;
BIS (R2)+,R5; 042205 — R5 |= *R2++;
BIS @-(R2),36(R5); 045265 000036 — (два слова) — *(R5+036) |= **-R2;
BIS @4(R2),36(R5); 047265 000004 000036 — (три) — *(R5+036) |= **(R2+4);
В ассемблерных мнемониках однобайтовый размер операнда указывается
добавлением после названия команды буковки Б-латинская. Например:
BIS (R2)+,R5; код 042205 — операция с целыми словами
BISB (R2)+,R5; 142205 — операция с байтами; содержимое R2 здесь
увеличивается не на 2 как в первом случае, а только на 1.
BISB @(R2)+,R5; 143205 — здесь опять на два, т.к. R2 указывает на адрес
BISB (R6)+,R5; 142605 — и здесь тоже на два: т.к. R6 — указатель стэка,
а в стэке даже один байт должен занимать целое слово
Операнды обозначаются не только как описано чуть выше, но и с
использованием вводимых в ассемблере имён. Каковые имена в основном заменяют в
нём адреса ячеек памяти (на момент написания программы еще неизвестные) и
играют ту же самую роль, что и метки в языке Си. По крайней мере вводятся в
точности так же — имя и после него двоеточие. Но в Си роль метки сильно
ограничена — единственное что с нею можно сделать — использовать в операторе
goto. Имена переменных тоже "помечают" собою отведенные под них ячейки памяти
(хотя и вводятся совершенно по-другому), ну так в ассемблере между ними и
метками разницы нет — машина же не различает что в ячейке — подлежащая обработке
информация или предназначенная для этого команда. За сим будущую переменную
точно так же помечаем меткой как и команду, коей собирается передавать
управление, а чтобы застолбить под неё место — пишут там начальное значение
(с помощью специально для этого предназначенной ассемблерной директивы). Но
можно и напрямую сопоставить (присвоить) число имени, например адреса регистров
консольного терминала. Хотя по-мне — указать его в явном виде — понятнее.
(Двойная работа: вместо одного числа (одного и того-же на все времена) нужно
еще запоминать и обозначающее его имя, у каждого автора своё собственное!)
Однако, рассматривая вышеперечисленные методы адресации, остаётся непонятным
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |