Алексей Вторников
Мал, да удал: мини-комьютер PDP-8
А вы ноктюрн сыграть могли бы на флейте водосточных труб?
Владимир Маяковский
Что бы вы ответили человеку, который поставил перед вами задачу написания программного обеспечения контроля за состоянием атомного реактора, используя в качестве платформы, скажем, современный мобильный телефон? Почти наверняка можно предположить, что такого человека сочтут несколько неадекватным.
Однако если обратиться к событиям 60-х годов XX века, то обнаруживается, что такое программное обеспечение было разработано. И разработано, кстати, вполне успешно, что доказывает опыт эксплуатации весьма больших и опасных объектов.
Рассматриваемый в этой статье компьютер интересен не столько с исторической точки зрения, сколько оригинальными решениями, воплощенными при его проектировании, которые могут оказаться поучительными и полезными для современных разработчиков как программного, так и аппаратного обеспечения.
Пролог и dramatic personae
Вначале были мини-компьютеры PDP-4 (1962) и PDP-5 (1963), разработанные выдающимся инженером и исследователем Гордоном Беллом (Gordon Bell). PDP-5 был экспериментальным проектом, но его архитектура оказалась столь удачной, что было произведено около 1000 экземпляров, что для опытных образцов необычно много. Работы было решено продолжить, и в 1965 году на основе PDP-5 Уэсли А. Кларком (Wesley Allison Clark) и Чарлзом Э. Молнаром (Charles Edwin Molnar) был разработан мини-компьютер PDP-8.
PDP-8 выпускался американской корпорацией Digital Equipment Corporation (DEC) до второй половины 70-х годов прошлого столетия в различных модификациях (PDP-8/S, PDP-8/I и проч.), отличавшихся в основном объемом установленной памяти, элементной базой и набором периферийного оборудования (см. рис. 1). В ряде стран выпускались клоны PDP-8 (например, мини-ЭВМ «Электроника-100И», выпускавшаяся в СССР).
Рисунок 1. Передняя панель (панель управления) мини-компьютера PDP-8/I
Интересной особенностью мини-компьютеров PDP-8 (как, впрочем, и большинства компьютеров того времени) было то, что панель управления отражала их внутреннюю архитектуру: наборы индикаторов и тумблеров позволяли следить за содержимым регистров и памяти, а также вводить программы в машинных кодах и управлять ходом выполнения программы.
На левой панели находилась распечатка машинного кода для загрузки компьютера; при необходимости этот код можно было ввести вручную и инициализировать систему.
На правой панели отображалась текущая выполняемая инструкция.
Набор тумблеров (вернее, часть из них) в нижнем ряду позволяет управлять специальным регистром (switch register или SR) для задания пары «адрес памяти + данные» с последующим их вводом в ячейки памяти.
С правой стороны управляющей панели (между индикаторами и тумблерами регистра SR) располагались управляющие кнопки.
Быстродействие PDP-8 по современным меркам было невелико (до 385 тысяч операций сложения в секунду), что обусловлено, конечно, в первую очередь их элементной базой: магнитная память на ферритовых сердечниках и транзисторы в схемах процессора и устройства управления. В последних модификациях PDP-8 уже применялись интегральные микросхемы, но, к сожалению, век PDP-8 заканчивался.
Во-первых, потому что DEC полностью переключилась на выпуск 16-битных мини-компьютеров PDP-11 (впоследствии 32-битных компьютеров VAX).
Во-вторых, в связи с бурным развитием микроэлектроники стало возможным производить основные компоненты компьютеров – процессор и память – с гораздо меньшими затратами.
Стойки PDP-8 стали постепенно исчезать, сменяясь более новыми, более мощными и более дешевыми компьютерами, и настал день, когда PDP-8 был снят с производства. PDP-8 продолжали «трудиться» еще несколько лет, но все было кончено – компьютер уходил в прошлое. Но для своего времени PDP-8 оказался настоящим прорывом – никогда до этого компьютеры не производились в таких масштабах.
С самого начала PDP-8 проектировался как компьютер общего назначения, пригодный не только для контроля и управления оборудованием, но и для решения широкого спектра задач: системного программирования (в том числе ОС разделения времени – предтеч современных многопльзовательских ОС), обработки научных и телеметрических данных, бизнес-приложений и впоследствии даже игр.
В отличие от других компьютеров той славной эпохи, PDP-8 производился серийно: всего было выпущено около 50000 машин.
В производстве PDP-8 был применен ряд технологических новшеств, которые позволили DEC установить на PDP-8 цены ниже цен других производителей компьютеров. Мини-компьютеры PDP-8 (впрочем, как и все, что разрабатывалось и производилось DEC) отличались надежностью, удобством эксплуатации, широким спектром применения и расширяемостью.
Техническое замечание
В дальнейшем изложении будет широко использоваться понятие позиционных систем счисления (двоичной, восьмеричной и десятичной). Читателям, забывшим, что это такое (не хочется думать, что найдутся такие, кто о них ничего не знает), правила перевода чисел из одной системы счисления в другую, а также понятия дополнений до 1 и до 2, рекомендуется освежить свои знания, воспользовавшись любым приличным учебником информатики; практически любая книга по программированию на ассемблере содержит всю необходимую информацию. Без этого многое в статье останется непонятным. Большую помощь могут оказать калькуляторы, способные работать в этих системах счисления. Система счисления будет указываться в скобках после числа, например, 5077(8).
Кое-где будут встречаться упражнения, требующие самой минимальной подготовки; рекомендую не пренебрегать ими и попробовать решить – польза будет несомненной.
Ответы и указания к упражнениям – в конце статьи.
Слова, слова, слова…
Первое, что бросается в глаза при знакомстве с PDP-8, это то, что в качестве основной единицы хранения и обработки информации выступают 12-битные слова. Никаких привычных современному программисту байтов нет и в помине (разумеется, возможность работы с отдельными битами или группами битов внутри слова имеется – без этого никуда).
Биты в слове перенумерованы от 0 до 11 слева направо (см. рис. 2).
Рисунок 2. Слово PDP-8 и порядок нумерации битов в нем
Такой порядок нумерации битов отличается от нумерации, принятой в большинстве процессоров, таких, например, как x86. Однако понятно, что это не более чем соглашение.
Для удобства программирования биты в словах принято разбивать на 4 группы по 3 бита в каждой: таким образом, число 110011100010(2) можно записать как 110 011 100 010(2). Обратившись к восьмеричной системе счисления (системе счисления по основанию 8, использующей цифры от 0 до 7), немедленно получаем 6342(8).
Легко видеть, что 12-битные слова ограничивают диапазон доступных адресов памяти значениями от 0 до 212-1= 4095(10), то есть всего 4096(10) ячеек памяти, что составляет всего 4096*12/8 = 6144 байт, или 6 Кб в пересчете на «стандартные» 8-битные байты. Этого, конечно, мало по любым меркам, в том числе и по меркам времени, когда создавался PDP-8. По сравнению с любым современным компьютером это совершенно ничтожная величина, однако ряд архитектурных решений и система команд позволили тем не менее превратить PDP-8 в один из самых успешных компьютеров. Кроме того, некоторые модели оборудовались памятью в 8 раз большей, то есть 32768(10) ячеек, чего было вполне достаточно для решения весьма нетривиальных задач.
Как и в привычных всем компьютерах, в PDP-8 и программы, и данные разделяют общую память («архитектура фон-Неймана»).
Одноадресная машина
Арифметические и логические операции в PDP-8 выполняются с использованием универсального 12-битного регистра – аккумулятора (accumulator), обозначаемого как «AC». Если операция (например, сложение или вычитание) предполагает наличие двух операндов, то первый из них находится в AC, а второй – в ячейке памяти. Результат операции остается в AC. Таким образом, в машинных командах может быть использован не более чем один операнд из памяти; поэтому PDP-8 относится к так называемым одноадресным машинам.
В дополнение к аккумулятору имеется однобитный регистр переноса, обозначаемый через «L» (от англ. link). Графически эта пара регистров изображается следующим образом (см. рис. 3) и кратко обозначается как «L-AC». 12-битный AC позволяет хранить числа со знаком в диапазоне от -2048(10) до 2047(10). Разумеется, это очень небольшие величины, и для обработки больших чисел нужно соответствующие операции либо эмулировать программно (что вовсе не сложно), либо использовать специальное оборудование. Отрицательные числа представляются в виде дополнения до 2: 7777(8) = -1(10), 7776(8) = -2(10) и так далее.
Рисунок 3. Регистры L и AC
Упражнение 1: проверьте, хорошо ли вы понимаете, почему отрицательные числа представляются именно таким образом? Найдите, например, восьмеричное и двоичное представления -1024(10).
При переполнении AC регистр L инвертируется (дополняется до 1), то есть его значение меняется с 0 на 1 и обратно. Отсюда следует, что по одному только текущему содержимому регистра L нельзя сказать, имело ли место переполнение AC или нет: это зависит от его предыдущего состояния. Например, пусть перед операцией прибавления 1 регистры AC и L были соответственно такими (для наглядности мы разбили регистр AC на 4 группы по 3 бита):
1 111 111 111 111
После прибавления к содержимому AC единицы его содержимое станет равным:
1000000000000
Однако поскольку это число превышает 12 бит, то самая левая единица оказывается «лишней» и регистр L инвертируется с 1 на 0. Окончательно:
0 000 000 000 000
Именно потому, что в команде можно сослаться не более чем на один операнд, PDP-8 не позволяет непосредственно сложить значения из двух ячеек памяти: для этого обязательно нужно использовать пару L-AC, последовательно загружая регистр AC содержимым из памяти и выполняя сложение.
Предварительные итоги
PDP-8 представляет собой одноадресный мини-компьютер, оперирующий 12-битными словами с использованием дополнения до 2 для представления отрицательных чисел. Доступное адресное пространство (в базовой конфигурации) ограничено 4096(10) словами (для некоторых моделей PDP-8 адресное пространство расширяется до 32767(10) слов). Все арифметические и некоторые логические операции производятся над содержимым пары L-AC. При написании программ используется преимущественно восьмеричная система счисления; данные в самом компьютере, разумеется, представляются как двоичные числа.
Типы инструкций
Самые интересные решения в архитектуре PDP-8 связаны с памятью – доступом и методами адресации. Их я буду описывать параллельно с системой команд компьютера: тем самым достигается более ясное понимание их взаимосвязи.
Система команд PDP-8 включает в себя три типа инструкций: инструкции, ссылающиеся на память (memory reference insrtuctions, MRI), инструкции ввода/вывода (IO) и так называемые микроинструкции (microinstructions).
Операции работы с памятью
Как вы думаете, сколько всего операций для работы с ячейками памяти предусмотрено в PDP-8? Уверен, большинству читателей ответ покажется невозможным: их всего 6! Формат слова, содержащего такую операцию, выглядит так (см. рис. 4).
Рисунок 4. Формат слова, содержащего операцию работы с памятью
Биты с 0 по 2 (обозначенные как «КОП») представляют собой код операции от 0 до 5 (от 000(2) до 101(2)). Операции работы с памятью перечислены в таблице.
Операции работы с памятью
Мнемоника
|
КОП
|
Описание
|
AND
|
000
|
Логическое «И» содержимого ячейки памяти и содержимого AC. Результат – в AC
|
TAD
|
001
|
Сумма содержимого AC и содержимого ячейки памяти. Результат – в АС
|
ISZ
|
010
|
Инкремент содержимого ячейки памяти. Если содержимое ячейки памяти стало равным 0, то пропустить следующую операцию
|
DCA
|
011
|
Выгрузка содержимого AC в ячейку памяти с последующей очисткой AC
|
JMS
|
100
|
Вызов подпрограммы
|
JMP
|
101
|
Безусловный переход
|
Рассмотрим остальные биты. Бит 3 (обозначен на рисунке как «К») – признак косвенной адресации: если он установлен в 1, то содержимое ячейки памяти интерпретируется не как операнд, а как адрес операнда. Бит 4 (обозначен как «С») – это страница памяти. Вот здесь нужно остановиться подробнее.
Память PDP-8 (как мы помним, в базовом варианте PDP-8 ее размер составляет 4096(10) последовательно перенумерованных слов) разбита на 32(10) страницы, размером 128(10) слов каждая.
Каждая страница, разумеется, имеет свой номер от 0 до 31(10) (в восьмеричной записи до 37(8)). Последние 7 бит слова, содержащего команды (с 5-го по 11-й), – это смещение адреса внутри страницы.
Упражнение 2: убедитесь, что указанные 7 бит смещения действительно могут адресовать 128(10) слов внутри страницы.
Вернемся к биту 4. Очевидно, что он может принимать только два значения: 0 или 1. Если бит 4 равен 0, то это означает, что смещение указывает на адреса, входящие в состав нулевой страницы (т.е. на первые 128(10) адресов памяти с абсолютными значениями от 0 до 127(10)).
Если бит 4 равен 1, то это означает, что смещение указывает на адреса, входящие в состав той страницы памяти, на которой располагается сама команда. Лучше все это разобрать на небольших примерах.
Допустим, аккумулятор содержит значение:
000000111100(2) = 74(8) = 60(10)
и процессор PDP-8 выполняет команду (число слева указывает на абсолютный адрес расположения команды в памяти) (cм. рис. 5).
Рисунок 5. Пример операции
Упражнение 3: какой странице памяти соответствует абсолютный адрес 200(8)? Определите двоичное и десятичное значения адреса этой страницы.
Разберем команду на составляющие (здесь для наглядности я отступаю от принятого ранее соглашения разбивать слово на 4 группы по 3 бита).
Первые три бита (011) – это код операции DCA (выгрузить содержимое AC по указанному адресу, после чего AC очистить).
Бит 3 равен 0; это означает, что косвенная адресация не используется.
Бит 4 равен 0, что, как мы помним, означает страницу 0.
Наконец, смещение (биты с 5 по 11) представляет собой число 7(8) = 7(10). Как процессор «справится» с такой командой?
Главное – это вычислить адрес ячейки памяти, по которому будет сохранено текущее значение аккумулятора.
Так как номер страницы, указанный в команде, равен 0, то смещение указывает на адрес 000 000 000 007(2) (проверьте) и именно по этому адресу и будет сохранено значение AC. Предыдущее значение, хранившееся в этой ячейке памяти, будет утеряно.
Если бит 4 установить в 1, то эта команда будет выполнена уже иначе. Поскольку бит 4 равен 1, а сама команда располагается на первой странице памяти по адресу 200(8), что соответствует абсолютному адресу 000 010 000 000(2), то после выполнения команды содержимое AC будет сохранено по адресу страницы плюс смещение внутри страницы, то есть по адресу 207(8).
Предыдущее значение, хранившееся в ячейке памяти с адресом 207(8), будет утеряно. Наконец, в обоих случаях содержимое AC после выполнения команды станет равным 0.
Бит 3 (косвенная адресация) позволяет организовать еще более гибкий способ доступа к ячейкам памяти. Разумеется, использование косвенной адресации несколько увеличивает время доступа к нужной ячейке памяти, поскольку, грубо говоря, необходимо найти адрес по адресу (позже, при обсуждении механизма подпрограмм, я вернусь к этому вопросу).
Страница 0 доступна всегда и из любого места программы и занимает в некотором роде привилегированное положение. Остальные страницы называются текущими.
Биты 3 и 4 могут комбинироваться в соответствии с логикой программы и алгоритмом (то есть возможны 4 способа адресации). Это немного по сравнению с современными процессорами, но для практических целей вполне достаточно.
Кроме того, PDP-8 позволяет организовать так называемое автоиндексирование – полезный способ адресации при организации циклов. Для использования автоиндексации на странице 0 резервируются несколько ячеек памяти. Я, однако, останавливаться на этих деталях не буду. Заинтересованный читатель может найти всю необходимую информацию, обратившись к ссылкам, приведенным в конце статьи.
И это все?
Внимательный читатель наверняка спросит: а как в PDP-8, например, выполнить вычитание – ведь такой операции в системе команд не предусмотрено? Верно, не предусмотрено. И не только вычитания, но ряда других полезных и необходимых операций, как, например, пересылки данных из одной ячейки памяти в другую. Но так ли необходимо в системе команд иметь, скажем, команду вычитания? Если подумать, то совсем необязательно: достаточно представить вычитаемое в виде дополнения до 2 и выполнить сложение. Так же просто решается задача пересылки данных из ячейки памяти А в ячейку памяти В: нужно последовательно очистить содержимое AC (как это сделать я расскажу дальше), загрузить AC данными из ячейки А (см. операцию TAD), а затем сохранить содержимое AC в ячейку В (см. операцию DCA).
Отдельно необходимо сказать о вычислениях с плавающей запятой. В базовой комплектации PDP-8 поддерживал только целочисленную арифметику, чего для управляющих процессов и научных расчетов было явно недостаточно. Была разработана специальная библиотека для эмуляции этих операций. Кроме того, в большинстве моделей PDP-8 был предусмотрен специальный регистр для расширения аккумулятора AC и позволявший умножать и делить большие числа. Путем подключения к PDP-8 специальных блоков операций с плавающей запятой можно было значительно увеличить скорость вычислительных операций.
Программы на PDP-8 получаются, таким образом, несколько длиннее аналогичных программ для привычных двухадресных компьютеров с более развитой системой команд.
Спору нет, такой несколько «пуританский» подход к системе команд возлагает на программиста дополнительные заботы, но давайте вспомним время, когда появился PDP-8.
Память представляла собой массив миниатюрных ферритовых сердечников с намотанным на каждом сердечнике проводом. При пропускании электрического тока в том или ином направлении ферритовый сердечник мог намагничиваться в одном из двух направлений – так реализовывалось хранение информации в двоичном представлении. Один сердечник соответствовал одному биту. Несложный подсчет показывает, какое количество сердечников содержала память в базовом варианте PDP-8: 12*4096 = 49152(10)! А ведь каждый сердечник необходимо было намотать проводом, распаять на плате, подвести управляющие шины... Понятно, что борьба шла за каждый бит памяти и поэтому система команд была минимальной – все, от чего можно было отказаться (пусть даже путем дополнительных затрат труда программиста), исключалось. Сегодня это трудно представить, но по тем временам это был весьма разумный подход.
Инструкции ввода/вывода
Для организации ввода/вывода в PDP-8 предусмотрена одна единственная инструкция. Вот ее формат (см. рис. 6).
Рисунок 6. Формат слова, содержащего операцию ввода/вывода
Из рисунка видно, что код операции ввода/вывода – всегда 6. Биты с 3-го по 8-й задают устройство (клавиатуру, принтер, ленточный или дисковый накопитель и так далее). Всего к PDP-8 можно было подключать до 64(10) устройств (почему?). Скажем, клавиатура имеет номер 000011(2).
Биты с 9-го по 11-й определяют одну из восьми операций ввода/вывода. Обычно предусмотрено до 3-х операций для каждого устройства, но если их должно быть больше, то в битах 9-11 остается некоторый «запас».
Устройства ввода/вывода могут управляться как напрямую, так и по прерываниям (прерывания могут оказаться необходимыми и для других целей, например, при разработке операционных систем).
Запрещение и разрешение прерываний кодируются этой же инструкцией: для этого достаточно выбрать устройство ввода/вывода с номером 0. Тогда, если в битах с 9-го по 11-й содержится комбинация 001(2), то будет включен режим разрешения прерываний, а если комбинация 010(2), то прерывания будут запрещены.
Опытные программисты хорошо знают, что программирование устройств ввода/вывода одна из наиболее сложных задач, поэтому за неимением места я не буду больше останавливаться на обсуждении вопросов ввода/вывода в PDP-8 и направляю заинтересованного читателя к ссылкам в конце статьи.
Микроинструкции
Микроинструкции – это, пожалуй, самый многочисленный «отряд» операций PDP-8. Эти операции не ссылаются на память, а работают в основном с парой L-AC. Для всех микроинструкций зарезервирован один код операции: 111(2) = 7(8). Микроинструкции разделены на два класса, отличающиеся один от другого состоянием бита 3: при его нулевом значении биты с 4-го по 11-й задают микроинструкции 1-й группы, в противном случае – 2-й группы. Подробно останавливаться на всех микроинструкциях я не буду – во-первых, их много, во-вторых, их назначение будет понятно из нескольких примеров. Рассмотрим две операции первой группы микроинструкций: очистка AC и очистка L. Их мнемоники, соответственно, CLA и CLL. В двоичном представлении (см. рис. 7, 8).
Рисунок 7. CLA
Рисунок 8. CLL
А в восьмеричном, соответственно, 7200 и 7100. Эти две операции можно объединить в одну, записав как «CLA CLL» (см. рис. 9).
Рисунок 9. CLA CLL
Упражнение 4: определите восьмеричный код такой операции.
Очевидно, что этим достигается значительная экономия памяти, поскольку позволяет объединить в одном слове несколько инструкций, которые процессор PDP-8 выполнит, когда до них дойдет время.
Более подробно останавливаться на микроинструкциях в этой статье нет смысла – всю информацию читатель сможет найти по ссылкам в конце статьи. Единственное, что нужно отметить, что операции 1-й группы микроинструкций «обслуживают» в основном пару L-AC, устанавливая в них заданные значения, в то время как операции 2-й группы проверяют содержимое регистров L и AC на соответствие определенным условиям и позволяют организовать гибкие ответвления и переходы (путем пропуска следующей команды в потоке выполнения). Например, микроинструкция второй группы SZA пропускает следующую инструкцию в потоке управления, если содержимое AC равно 0; вот ее двоичное представление (см. рис. 10). Если содержимое AC равно 0, то инструкция, следующая за SZA, будет пропущена.
Рисунок 10. L-AС
Упражнение 5: определите восьмеричный код этой операции.
Подпрограммы
Перед тем как завершить обзор архитектуры PDP-8, имеет смысл немного задержаться на подпрограммах, их вызовах и, главное, на возврате из подпрограмм в вызывающий код.
Причина, почему я на этом останавливаюсь, проста – в PDP-8 нет стека. Конечно, его можно эмулировать программно, но это совсем другой вопрос. Главное, что аппаратный стек в архитектуре PDP-8 отсутствует. Плохо это или нет, я здесь обсуждать не буду (скорее плохо, поскольку стек очень уж «полезная» часть архитектуры компьютера). Как же в таком случае использовать подпрограммы (вспомните, при обсуждении операций работы с памятью я перечислил в числе прочих и операцию вызова подпрограммы)? Передать управление подпрограмме, очевидно, несложно: по сути, это безусловный переход в нужную точку программы. А вот как вернуться назад, не имея стека, проще говоря – где и как сохранить адрес возврата?
Можно, конечно, выделить в памяти определенную ячейку памяти и перед вызовом подпрограммы сохранять в ней нужное значение программного счетчика. Но если вызовов подпрограмм несколько, то в этой ячейке будет сохранено только последнее значение программного счетчика – все остальные будут безвозвратно затерты.
Конструкторы PDP-8 нашли элегантное, хотя и необычное, решение: адрес возврата должен храниться в самой подпрограмме! Конечно, это не стек (скажем, при таком способе организации подпрограмм неизбежно возникнут проблемы с рекурсивными вызовами), но зато при таком способе подпрограмма сама «знает», куда надо передать управление по окончании своей работы. Проще показать все это на примере (для наглядности в левой колонке я указал восьмеричные адреса памяти, по которым располагается этот фрагмент программы) (см. рис. 11).
Рисунок 11. Программа (фрагмент), демонстрирующая вызов подпрограммы
Хотя я и не рассказывал, что из себя представляет программа на ассемблере для PDP-8, думаю, ничего сложного в примере нет, тем более что все будет сейчас объяснено.
По адресу 1210(8) происходит вызов подпрограммы. Подпрограмма начинается с ячейки памяти, помеченной как «SUBRT» (заметьте, что в ассемблере PDP-8 метки отделяются от остального кода запятыми) по адресу 1240(8). Итак, переход к подпрограмме осуществлен.
Ячейка памяти, имеющая метку SUBRT, предназначена для одной цели – в ней удерживается (сохраняется) адрес команды, непосредственно следующей за командой вызова подпрограммы (в данном случае это адрес 1211(8) инструкции «JMP BEGIN»), то есть по существу адрес возврата. Любое значение, хранившееся в ячейке 1240(8) до вызова подпрограммы, будет затерто значением 1211(8). Фактически подпрограмма начинает выполняться с операции, следующей за точкой входа в подпрограмму, то есть с адреса 1241(8); какую именно задачу решает подпрограмма в примере, для нас в настоящее время не существенно.
По адресу 1255(8) – последней команде в подпрограмме – находится команда безусловного перехода с использованием косвенной адресации (на это указывает символ «I»).
Упражнение 6: перед тем как читать дальше, подумайте – куда будет передано управление после исполнения команды «JMP I SUBST»? Попытайтесь вручную оттранслировать этот фрагмент программы.
Управление будет передано по адресу, хранящемуся в ячейке памяти SUBRT, то есть, как мы помним, по адресу 1211(8). Итак, для выхода из подпрограммы и возврата к основной части программы необходим так называемый косвенный переход – переход по адресу, хранящемуся не в самой команде, а в ячейке памяти.
Шина OMNIBUS
Рассказ о мини-компьютерах PDP-8 был бы неполным без упоминания такой важной его составляющей, как шина OMNIBUS. Основное назначение шины OMNIBUS состояло в передаче команд и сигналов между модулями PDP-8. Физически OMNIBUS представляла собой плату с коннекторами и слотами для присоединения различных устройств. Коннекторы позволяли организовать доступ к адресной шине, шине данных памяти и управления, шине данных, прямой доступ к памяти (доступ к памяти, минуя процессор), сигналы таймера и так далее (всего 96 сигналов).
Знакомство с шиной OMNIBUS необходимо прежде всего при программировании операций ввода/вывода и управления периферийными устройствами, подключаемыми к PDP-8 (датчиками, приборами и прочее). Поскольку, как я уже упоминал, это одна из наиболее сложных задач, стоящих перед программистом, заинтересованный читатель может обратиться к документации по ссылкам в конце статьи.
Программное обеспечение
Для мини-компьютера PDP-8 и его модификаций был создан весьма внушительный объем программного обеспечения. Существовало объединение разработчиков и пользователей ПО для мини-компьютеров, выпускавшихся DEC (не только для PDP-8): DECUS (Degital Equipment Corporation User's Society). DECUS координировало процесс разработки ПО, обеспечивало его участников доступом к существующему ПО и к важной технической и технологической информации.
Прежде всего я расскажу о системном ПО, включавшем ассемблеры, компиляторы языков программирования и операционные системы.
Ассемблеры
Для PDP-8 были разработаны 4 варианта ассемблера. Ассемблер PAL-III представлял собой ассемблер для моделей, оборудованных базовым объемом памяти 4096 слов. Его расширенная модификация MACRO-8 позволяла определять в программах макро; ассемблер поддерживал двойную точность арифметических операций, операции с плавающей точкой, литералы и некоторые другие расширения. Ассемблер PAL-D предназначался исключительно для использования на модели PDP-8/I совместно с дисковым монитором (disk monitor system). Наконец, ассемблер SABR предназначался для программирования на моделях с объемом памяти больше 4096 слов и обладал самыми лучшими возможностями.
Кроме ассемблеров, с PDP-8 поставлялись несколько отладчиков, среди которых наиболее широкими возможностями располагал отладчик ODT-8 (octal debugging technique), предшественником которого (разумеется, с гораздо более скромными возможностями) был отладчик ODT, разработанный для PDP-5.
Компиляторы и интерпретаторы
Для PDP-8 были написаны компиляторы самых распространенных в то время языков: FORTRAN, ALGOL и BASIC.
Корпорацией DEC был разработан язык высокого уровня FOCAL, предназначенный в основном для инженерных и научных расчетов. Язык позволял не только производить расчеты, но располагал средствами построения простейших графиков. Разумеется, все модели PDP-8 были снабжены интерпретатором FOCAL.
Из интерпретаторов следует упомянуть язык функционального программирования LISP и язык обработки текстов SNOBOL.
Операционные системы
Для PDP-8 были разработаны несколько операционных систем.
Прежде всего необходимо сказать несколько слов о самой первой операционной системе для PDP-8: OS/8. Эта операционная система была весьма простой, но в то же время и весьма компактной: резидентная часть OS/8 занимала всего 256 слов памяти. Прерывания при вводе/выводе не использовались. В качестве средства «общения» между пользователем и оборудованием, OS/8 предоставляла язык командной строки (concise command language).
Далее, следует упомянуть дисковый монитор (disk monitor system) – программу управления PDP-8, рассчитанную на ввод команд с использованием терминала. Его функциональность была довольно ограниченной, но, тем не менее, он существенно облегчал работу пользователя с мини-компьютером PDP-8, системными программами и периферийным обрудованием. Дисковый монитор предоставлял весьма широкие возможности по работе с файлами, прежде всего с файлами на жестких дисках, что существенно ускоряло и облегчало работу пользователей.
Третья операционная система, которую я отмечу, это операционная система разделения времени (time-sharing system) TSS/8. Для работы TSS/8 требовалось не менее 12288 слов памяти, из которых первые 8192 слов предназначались для размещения программ монитора операционной системы, а оставшиеся 4096 слов разделялись между пользователями системы. TSS/8 обслуживала одновременную работу нескольких пользователей, выделяя каждому из них необходимые ресурсы (память, дисковое пространство) и контролируя их права.
Прикладное программное обеспечение
Прикладное программное обеспечение, написанное для PDP-8, также было самым разнообразным. Можно упомянуть, к примеру, программное обеспечение контроля производственных объектов и процессов, сбора и обработки информации, систем телеметрии и мониторинга, программное обеспечение для бизнеса и даже игры.
Одним словом, PDP-8 славно выполнял свою работу, был надежной и простой в обслуживании и управлении машиной (чего, к сожалению, нельзя сказать о некоторых его клонах, выпускавшихся во многих странах, в том числе и в бывшем СССР).
Ответы и указания к упражнениям
Ответ к упражнению 2 содержится непосредственно в тексте статьи.
Упражнение 1
Двоичное представление числа -1024(10) определяется следующим образом:
- сначала найдем двоичное представление положительного числа 1024(10) = 010000000000(2), записав его в виде 12-битного слова с ведущим 0;
- затем определим дополнение до 1 (заменой 0 на 1 и наоборот): 1011111111(2);
- и наконец прибавим 1 для дополнения до двух: 110000000000(2) = -1024(10).
Теперь получить соответствующее восьмеричное представление совсем просто (заменой справа налево групп из троек двоичных чисел соответствующими восьмеричными): 6000(8).
Для проверки сложим начальное и конечное двоичные представления; если преобразование выполнено верно, то сумма должна быть равна 0:
0100 0000 0000
1100 0000 0000
--------------
1 0000 0000 0000
Перенос в несуществующий 13-й разряд игнорирутся; таким образом, в результате получен 0, и преобразование выполнено верно.
Аналогичная проверка для восьмеричных представлений 2000(8) + 6000(8) = 10000(8) дает тот же самый результат.
Примечание: на вопрос, почему игноририруется перенос в 13-й разряд и не является ли это ошибкой, ответ можно найти, обратившись к литературе по представлениям чисел в различных системах счисления (см. выше).
Упражнение 3
Абсолютному адресу 200(8) соответствует 1-я страница памяти. Соответствующие представления: 000010000000(2) или 128(10).
Упражнение 4
Ответ: 7300(8).
Упражнение 5
Ответ: 7440(8).
Упражнение 6
Это упражнение – самое сложное, но и самое интересное. Читателю, чтобы его решить, возможно, придется обратиться к более подробному описанию мини-компьютера PDP-8, которое можно найти по ссылкам в конце статьи.
Рисунок 12. Программа (фрагмент) и результат ее трансляции в машинный код
Сначала я приведу результат трансляции (справа от исходного текста программы, см. рис. 12), а затем дам некоторые комментарии:
Прежде всего обращаю внимание, что программа занимает адреса, отведенные для пятой страницы памяти (т.е. в диапазоне от 1200(8) до 1377(8)). Иными словами – программа будет загружена не в нулевую, а в текущую (а именно – в пятую) страницу памяти, и это нужно учитывать при ее трансляции в машинные коды.
Разберем подробно, как транслируется самая первая команда программы:
- код операции JMS (биты с 0 по 2): 100(2);
- бит 3 сброшен в 0 (косвенная адресация не используется);
- бит 4 установлен в 1 (адресация на текущей странице);
- управление передается по символическому адресу (метке), расположенной в ячейке памяти 1240(8). Смещение этого адреса от начала страницы составляет 1240(8) – 1200(8) = 40(8) = 0100000(2);
- все составляющие машинного кода определены, и остается их «склеить» вместе: 100010100000(2) = 4240(8).
Обратите внимание, что после выполнения этого кода процессором PDP-8 в ячейке памяти с адресом 1240(8) будет сохранен адрес возврата (адрес ячейки памяти 1211(8)). Фактически подпрограмма начинает выполняться с адреса 1241(8).
Остальные команды транслируются аналогично. Обратите внимание на два момента. Во-первых, код по адресу 1240(8) будет оттранслирован просто в 0. Даже если по этому адресу и располагалась какая-то информация, она будет заменена адресом возврата немедленно после входа в подпрограмму. Во-вторых, разберем трансляцию в машинный код команды возврата из подпрограммы:
- код операции JMP (биты с 0 по 2): 101(2);
- бит 3 установлен в 1 (косвенная адресация);
- бит 4 установлен в 1 (адресация на текущей странице);
- управление передается по адресу, хранящемуся в ячейке памяти 1240(8). Вычислим смещение ячейки памяти с адресом 1240(8) от начала страницы: 1240(8) – 1200(8) = 40(8) или 0100000(2);
- окончательно, получаем ответ: 101110100000(2) = 5640(8).
Когда управление дойдет до этого участка, будет осуществлен переход по адресу, хранящемуся в ячейке 1240(8), в котором уже хранится адрес точки возврата, то есть адрес ячейки памяти 1211(8).
Эпилог
Разумеется, PDP-8 по своим возможностям не сравним с возможностями современных компьютеров. О таких объемах памяти и уровне быстродействия, которыми располагают компьютеры сегодня, разработчики и программисты PDP-8 могли только мечтать. Но, как всегда и везде, решающее значение имеют не столько технические возможности аппаратуры, сколько интеллект, любознательность, усидчивость и квалификация тех, кто с этой аппаратурой работает.
PDP-8 (как и ряд других компьютеров той славной эпохи) отчетливо демонстрируют, сколь многого можно добиться даже при очень скромных ресурсах, если приложить к своему делу голову.
История проекта PDP-8 показывает, что почти любую, не решаемую на первый взгляд задачу, можно решить и не нужно для этого, в общем-то, ничего – «всего лишь» думать. И, желательно, нестандартно.
- Большая подборка документации по архитектуре и программированию мини-компьютеров PDP-8 – http://bitsavers.org/pdf/dec/pdp8.
- MS-DOS эмулятор PDP-8 – http://www4.wittenberg.edu/academics/mathcomp/bjsdir/PDP8HomePage.htm.
- По этой ссылке читатель найдет несколько эмуляторов PDP-8 – http://www.aracnet.com/~healyzh/pdp8emu.html.
- FAQ по PDP-8 – http://faqs.cs.uu.nl/na-dir/dec-faq/pdp8.html.
- Страница Д.Джонса с многочисленными ссылками по PDP-8 – http://www.cs.uiowa.edu/~jones/pdp8.
- Интересная статья, посвященная истории PDP-8 – http://pcmag.ru/solutions/detail.php?ID=11528.
- Эта ссылка не имеет прямого отношения к PDP-8, однако здесь читатель найдет весьма поучительную историю, с которой раньше (да порой и сейчас) частенько приходилось сталкиваться программистам – http://www.wasm.ru/article.php?article=onebyte.