| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
А<=Б -> C=1 результат получится "отрицательный" — перенос выскочит
за пределы разрядной сетки
для чисел со знаком дело обстоит сложнее:
А>Б определяется по комбинации признаков N^V (здесь ^ — искл.ИЛИ)
а для А<=Б эта комбинация должна быть равна нулю (т.е. либо оба нули, либо
оба единички)
Команд ветвления, использующих эти признаки результата — восемь. Устроены
они так: младший байт — расстояние на которое надо перейти, если проверяемое
командой условие выполняется — однобайтовое число (со знаком), указывающее
количество слов, т.е. прибавляющееся к счетчику команд со сдвигом на один бит.
Один бит — состояние проверяемого признака, при котором надо переходить,
остальное — код операции (КОП).
К0КSSS по битам: К 000 кКК ПSS SSS SSS где S — смещение, П — признак, К — КОП.
0 04 BR 0 000 1 — "безусловный" переход — ничего не проверяет
0 1 BNE / BEQ 0 001 * — Z если равно / не равно (нулю)
0 2 BGE / BLT 0 010 * — N^V >=0 / <0 — для чисел со знаком
0 3 BGT / BLE 0 011 * — Z|(N^V) >0 / <=0 — для чисел со знаком
1 0 BPL / BMI 1 000 * — N если плюс / минус
1 1 BHI / BLOS 1 001 * — C&Z >0 / <=0 — для чисел без знака
1 2 BVC / BVS 1 010 * — V нету переполнения / есть
1 3 BCC / BCS 1 011 * — C нету переноса / есть
или BHIS / BLO — C >=0 / <0 — для чисел без знака
Здесь команды перечислены в порядке возрастания кодов операций (а не в
логическом порядке — типа сначала проверка отдельных признаков, потом их
комбинаций для сравнения чисел со знаком и без такового). Обратим внимание: один
бит кода операции (тот, который обозначен маленькой буквой к) неизменно ноль, зато
вместо него задействован старший (знаковый) бит слова. Это к тому, как шло
распределение пространства кодов команд.
r ккк xxx xxx yyy yyy — двухадресная команда — кроме ккк = 000 и 111
из того, что осталось:
1 111
* * *
* * *
* * — зарезервировано под команды ППЗ
к 000 0кк пss sss sss — команды ветвления К0К
* * *
(кроме к_ккп = 0_000)
* 000 1кк ккк ххх ххх — одноадресные команды, из них мы уже рассмотрели:
r ... .01 ккк ... ... — основные *05К**
r ... .10 0кк ... ... — команды сдвига *06К** (только 4 штуки)
значит пока что незадействованными (или нерассмотренными) остались коды:
* ... .10 1** — 4 штуки *064** — *067**
* ... .11
* * *
— 8 штук *07
* * *
еще пропущена команда с кодом 00 (с учетом старшего бита — 2 штуки)
* ... .00 ... ... ... *04
* * *
это будут JSR и EMT/TRAP
ну и совершенно еще не использованные
0 111
* * *
* * *
* * 07
* * *
а так же — то, что осталось от команд ветвления (к_ккп = 0_000)
0 000 000 0*
* * *
* * — т.е. команды с полностью нулевым старшим байтом
А ведь у нас еще совершенно не рассмотрены команды передачи управления.
Четыре блудных команды вида *064** — *067** (с учетом использования под код
операции знакового разряда — 8 штук) я всётаки насобирал как говорится "с бору
по сосенке":
— Команда SXT с кодом 0067ХХ (где ХХ — полноформатный операнд) — "расширение
знака" — заполнение старшего байта слова знаковым разрядом младшего. Т.е.
превращение однобайтового целого числа в двухбайтовое. (Кстати, при загрузке
байта в регистр это происходит автоматически.)
— Пара команд с кодами 1067ХХ и 1064ХХ — MTPS и MFPS — загрузка операнда ХХ в
слово состояния процессора и наоборот — сохранение ССП по указанному адресу.
У старших моделей они отсутствуют, т.к. это была бы "дырка" в механизме защиты.
(Вместо этого есть привилегированная команда SPL для установки уровня приоритета
и обычная команда с кучей разных названий для установки и сброса признаков
результата, имевшаяся впрочем и самых младших моделей.)
— За то у самой старшей модели (где всё по максимуму) есть четыре интересные
команды с кодами 0065ХХ, 1065ХХ, 0066ХХ и 1066ХХ — MFPI, MFPD, MTPI, MTPD,
занимающиеся пересылкой слова данных ХХ из предыдущего адресного пространства
на стэк текущего. И соответственно со стэка в. А две пары потому что адресные
пространства команд и данных там видите-ли раздельные. (Можно сделать, но не
обязательно. Впрочем речь об этом еще впереди.)
— И еще одна заблудившаяся команда MARK с кодом 0064NN, делающая что-то очень
хитрое. Впринципе это возврат из подпрограммы с параметрами (с одновременным
истреблением оных). Вот сейчас мы это и рассмотрим.
Впринципе для передачи управления в любую точку оперативной памяти (а не на
+/-127 слов как у команд ветвления) вполне хватило-бы команды пересылки MOV:
записал адрес точки перехода прямо в регистр R7 и всё. (В идейном потомке PDP-11
микроконтроллере MSP-430 так и сделано.) Эта возможность не исключается и здесь.
Но разработчики архитектуры сделали отдельную команду JMP с кодом 0001АА, в
которой АА, в отличии от команды MOV с кодом 01АА07 не указание того места, где
взять адрес по коему надо перейти (засунув его в R7), а того, где следующая
подлежащая выполнению команда. (Но если АА = 0х, то получается что эта команда
— в регистре Rх, что абсурд. Зачем так сделано — не знаю, разве что по аналогии
с командой передачи управления подпрограмме JSR.
Команда передачи управления подпрограмме JSR — полутораадресная. Её код
004RАА включает так же как и код команды JMP, указание куда передать управление,
а кроме того — номер регистра, где сохранить адрес возврата. Содержимое самого
этого регистра сохраняется на стэке. А командой возврата из подпрограммы RST с
кодом 00020R — восстанавливается обратно. Спрашивается, зачем так сложно? Почему
нельзя, как все люди, сразу сохранить адрес возврата на стэке, уж коли он есть?
(Впрочем, если в качестве этого регистра указать сам счетчик команд — так оно и
происходит.) Это ведь когда стэка еще небыло — адрес возврата вот так сохраняли
в регистре, а сейчас-то зачем? А это даёт дополнительную гибкость. Например
сразу после команды вызова подпрограммы можно поместить некую полезную
информацию для неё (ну хотя бы текстовую строку, которую оная программа должна
вывести на терминал в качестве диагностического сообщения). Теперь ей не надо
выковыривать из стэка адрес где это находится — он уже в регистре;
попользовалась, сдвинув оный регистр-указатель на первое слово после этой,
предназначенной ей информации — там как раз и будет следующая команда, которой
надо возвернуть управление. Очень удобно.
Уже упоминавшаяся хитрая команда MARK с кодом 0064NN служит для "маркировки"
конца списка параметров и/или локальных переменных подпрограммы (NN штук)
завалявшихся на стэке и при возврате из оной подпрограммы подлежащих удалению.
Она помещается на вершину стэка самой подпрограммой, которая обязательно должна
получать управление командой JSR R5, AAAA; и которая для возврата управления
должна употребить команду RST R6. Тогда R7 <— R6, а там как раз и лежит вот эта
самая команда MARK NN, она делает: R6 <— R7+NN*2; R7 <— R5; R5 <— (R6)+.
Еще к командам передачи управления относятся "командные прерывания"
предназначенные прежде всего для обращения к операционной системе (за услугами)
или каким другим системным программам (например к отладчику или некоему
супервизору ввода/вывода). Не в пример писишке каждой из таких команд поставлен
в соответствие один единственный фиксированный вектор прерывания. Который ни при
каких обстоятельствах не используется ни для чего другого. Таковых команд
четыре: EMT и TRAP с кодом 104ХХХ отличаются одним битом (младшим во втором
байте), а весь младший байт отведён под код запроса — на выполнение команды он
никак не влияет. (То есть получившая прерывание системная программа должна
слазить в то место, куда указывает адрес возврата, взять младший байт команды и
отработать ту функцию, на которую он намекает.) Не знаю почему их две — наверно
просто так коды распределились. Однако одни операционные системы (например
RT-11) используют EMT, а другие (например UNIX) — TRAP. Может у кого-то была
мысль сделать под UNIX`ом виртуальную RT-11? Их вектора 030 и 034.
Два других командных прерывания BPT и IOT с векторами 014 и 020 и кодами
00003 и 00004 якобы предназначены для отладчика и подсистемы ввода/вывода.
(Ну типа отладчик организует в отлаживаемой программе точку останова подменяя
одну из её команд на BPT, а как получит по ней управление — незаметно подменяет
её обратно. Ну а с вводом/выводом-то как?)
Следует иметь в виду, что при прерывании в стэке сохраняется не только
счетчик команд, но и слово состояния процессора. А новые загружаются из вектора
прерывания. (Потому-то они и идут через четыре байта — эти самые четыре байта и
загружаются.) Следовательно и возврат из прерывания должен производиться не как
из подпрограммы, а специально на то предназначенной командой. Каковых две:
000002 RTI — просто возврат из прерывания — без всяких затей
000006 RTT — хитрый возврат из прерывания — специально для отладчика
Тут дело вот в чём: в слове состояния процессора есть такой специальный
признак Т. Если он равен нулю — всё как у людей. А вот если его установить в
единицу — сразу же будет прерывание по тому же самому вектору 014 что и по
команде BPT. А установиться в единицу он может например по команде возврата из
прерывания (ну вот такое слово попало в ССП из стэка). А отладчику, желающему
прогнать программу по шагам, надо чтобы не сразу, а только после того, как
выполнится одна команда. Вот для этого он RTT и использует.
Еще к командам управления можно отнести следующие:
000000 HALT — стоп — останавливает процессор (привилегированная!)
000001 WAIT — ждать — висит на этой команде пока не произойдёт прерывание
000005 RESET — сброс — сброс всех внешних устройств в некое исходное
состояние — в старших моделях ясный пень тоже привилегированная, потому как
приводит в состояние изумления не только всякие там принтеры с терминалами (ну
или контроллеры дисководов, ежели они есть), но и такую важную штуку, как
диспетчер памяти. И буде учинить её в защищенном режиме — одному Аллаху известно
к чему это приведёт.
Еще есть такая привилегированная команда SPL с кодом 00023N -
устанавливающая уровень приоритета прерываний N (буде командами MTPS / MFPS
напрямую читающей и пишущей обратно целиком всё слово состояния процессора
воспользоваться нельзя). И всё — кроме этих трёх других "привилегированных"
команд даже в самой старшей модели нет. (А привилегированность их заключается в
том, что в "системном" режиме они успешно выполняются, а в "пользовательском" -
нет — вызывают прерывание. По-моему по вектору 010, по которому вообще
происходит прерывание при попытке выполнить неизвестную данному процессору
команду.)
Ну про что я еще забыл, прежде чем перейти к "расширенным" командам? Ну
конешно про сброс и установку наших любимых признаков NZVC! Команда эта с кодом
00024Х точнее по битам: 0 000 000 010 1yX XXX где y указывает что именно
нужно сделать — установить указанные единичками в битах ХХХХ признаки NZVC (вот
именно в таком порядке), или же наоборот сбросить. Названий у этой команды -
пруд пруди, в том числе NOP (ничего не делать) — это если все ХХХХ нулевые.
000240 NOP 000260 NOP ничего не делать
000241 CLC 000261 SEC сбросить/установить признак C
000242 CLV 000262 SEV -//— V
000244 CLZ 000264 SEZ -//— Z
000250 CLN 000270 SEN -//— N
000257 CCC 000277 SCC сбросить/установить всё
Ну и еще команда SWAB с кодом 0003АА — перестановка байтов в слове. Она вроде
бы тоже не расширенная а из основного набора.
Подводя промежуточный итог, отметим, что базовый набор команд это
необходимый минимум. Он позволяет выполнить любые требуемые действия, но
содержит только самые элементарные операции. Ни умножения с делением ни тем
более операций с плавающей запятой там нет. Их придётся эмулировать.
"Расширенные" команды — полутораадресные: здесь почти все команды (кроме
исключающего ИЛИ, ну и возможно одинарного многобитного сдвига, которые надо бы
включить в основной набор двухадресных команд, да вот к сожалению элементарно
кодов не хватило) используют пару соседних регистров (обозначено R и R'),
отличающихся младшим битом номера. (Т.е. если указан R2, то второй конешно же
будет R3, а вот если указан R3, то R' будет вовсе не R4, а R2!)
070RАА MUL — целочисленное умножение R*A -> (R,R')
071RАА DIV — целочисленное деление (R,R') / A -> R, R' (остаток)
072RАА ASH — арифметический сдвиг на несколько разрядов (6 бит операнда А)
073RАА ASHC — сдвиг на несколько разрядов двух регистров R и R'
074RАА XOR — исключающее ИЛИ (инверсия битов) R^A -> A
Стэковая плавающая арифметика одинарной точности
07500R FADD — сложение
07501R FSUB — вычитание
07502R FMUL — умножение
07503R FDIV — деление
Регистр R — указатель вершины операционного стэка, с которого берутся
(черырёхбайтовые в данном случае) операнды и куда кладётся результат. Как видим,
здесь свободных кодов — полным-полно (еще 60 штук: 07504R — 07577R) — вполне
хватит соорудить полноценный стэковый сопроцессор. Но в эту сторону развитие
архитектуры не пошло. А с реализацией ППЗ и эти команды упразднили.
Команды вида 076ХХХ тоже нигде не задействованы.
077RNN SOB — цикл со счетчиком: счетчик (регистр R) уменьшается на единицу,
и если получился не ноль — производится переход назад на NN слов.
Вот собственно и вся система команд.
Ах да, еще кажется у Э-85 завалялась такая команда MFPT с кодом 000007,
записывающая в R0 некую константу, содержащую код модели процессора в младшем
байте и что-то такое еще — в старшем. Уж и не знаю зачем.
А команды процессора с плавающей запятой мы рассматривать пока не будем: есть
вещи и по-интереснее. Диспетчер памяти например. И вообще структура памяти.
Ну что можно сказать про память? Адресное пространство — линейное (или
"плоское"). Размер адреса равен разрядности процессора и размеру машинного
слова. Причём в виду наличия развитой системы косвенной адресации это
принципиально. То есть увеличить размер адреса в пределах данной архитектуры
невозможно — для этого надо делать другую машину с другой системой команд.
(Что и было сделано: VAX-11.) Впрочем на момент разработки (и следующие десять
лет) объём адресного пространства в 2^16 байт казался прямо-таки необъятным!
Оперативной памяти туда устанавливалось куда как меньше. Но как известно, дашь
коту сала, а ему всё ма-а-ало! И в конце-концов настали времена...
Впрочем еще раньше появились "старшие" модели, заточенные под многозадачный
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |