====== Кросс-система МИКРОБ ======
Лаборатория вычислительной техники и автоматизации
А. П. Сапожников
Дубна 1985 г.
|---------| /-----------------\ |------------| | текст | | м и к р о - | | описание | |программы|=====>|а с с е м б л е р|<=====| формата | | | | | |микрокоманды| |---------| \-|-------------^-/ |------------| | | | | | а р х и в | |----------v--------|----v------------| | библиотека | глобальный | | обьектных модулей | контекст | |----------|--------|-----------------| | | /-------v-------\ | загрузчик | \-------|-------/ | | |------v------| | двоичный код| |-------------|Кросс-система МИКРОБ имеет много общего со "взрослыми" системами программирования на языках высокого уровня: * допускается раздельная трансляция подпрограмм; * результат трансляции записывается в библиотеку модулей загрузки; * для получения готовой двоичной программы используется связывающий загрузчик; ===== 1. Вызов транслятора ===== Пакет задачи, запускаемой на БЭСМ-6 с целью трансляции микропрограмм, выглядит следующим образом:
*nаме .....
*pass:.....
*time:.....
*library:23,25
<заказ архива под номером 30>
<заказ, если надо, файла с текстом>
*call microb [ :<ук.мл> [ ,<список> ] ]
или:
*call micini [ :<ук.мл> [ ,<список> ] ]
В квадратные скобки [ ] здесь и далее мы будем заключать
необязательные параметры или синтаксические конструкции. Здесь
необязательный параметр <ук.мл> задает адрес начала файла, содержащего
тексты микропрограмм. Причем трансляции подлежат только микропрограммы,
указанные в списке. При пустом списке транслируется весь файл. При
отсутствии <ук.мл> текст читается из пакета задачи. Признаком конца
текста считается карта *end
MICROB - вход для трансляции. При этом считается, что описание формата
микрокоманды (т.н. Глобальный контекст) уже записано в архив ассемблера.
MICINI - вход при первоначально пустом архиве. При этом текст,
подлежащий трансляции, предваряется описанием формата микрокоманды.
===== 2. Трансляция глобального контекста =====
Глобальный контекст микропрограммы состоит из
* описания формата микрокоманды;
* определения глобальных имен;
* описания констант из ПЗУ;
Глобальный контекст оформляется в виде псевдопрограммы, имеющей имя
DEFINE:
define:prog n,к; - заголовок
..........
end; - конец
Здесь N задает разрядность микрокоманды, К - разрядность ПЗУ
констант. Биты в микрокоманде нумеруются справа налево:
N, N-1, ...,2,1. При неуказании К = 64.
Внутри псевдопрограммы DEFINE находятся инструкции, определяющие поля
микрокоманды и глобальные имена, которые считаются предописанными во
всех микропрограммах.
Несколько замечаний терминологического характера:
* <число> - целое десятичное, если нет специальных оговорок;
* <имя> - идентификатор в смысле алгола, длиной не более 6 символов;
* <выражение> - список термов, отделенных друг от друга знаками "+" или "-";
* <терм> - число, имя или специальный символ "*";
По аналогии с ассемблерами уровня 2, на язык так и просятся слова
"переменная" и "значение". Однако в микропрограммах не бывает рабочих
ячеек памяти, а содержимое полей микрокоманд не меняется в ходе работы.
Поэтому, говоря о микроассемблере, мы должны иметь в виду, что любое имя
в программе - это всего лишь мнемоника для обозначения числа или адреса.
Все-таки в дальнейшем, ради краткости, мы будем называть значением
выражения сумму его термов, взятых со своими знаками. Термы, входящие в
выражение, могут быть трех типов:
* число. Количество термов этого типа в выражении не ограничено;
* адрес относительно начала "своей" программы.
В правильном выражении должно выполняться соотношение
0 <= NP-NN <= 1
где NP - число относительных адресов со знаком "+",
NN - число относительных адресов со знаком "-";
* адрес внешней программы. В выражении допускается только один терм
этого типа, со знаком "+", и при условии, что NP = NN.
==== 2.1. Формат микрокоманды ====
Кроме разрядности N формат микрокоманды характеризуется еще набором
полей, ее составляющих. Для описания одного поля служит инструкция FIELD:
<ИМЯ ПОЛЯ>:FIELD N1,N2 [,N3 ]
* N1, N2 - числа, задающие левую и правую границу поля (в любом порядке);
* N3 - выражение, значение которого задает начальное содержимое поля. При неуказании N3 считается нулем;
Допускается вложение одних полей в другие, перекрытие полей не
допускается (аналогично фортранным DO - циклам). Порядок описания полей
- произвольный.
Для поля кода операции секвенсора в качестве параметра N3 необходим
указывать маркирующую конструкцию /С/
Для поля адреса микропрограммы в качестве параметра N3 необходимо
указывать маркирующую конструкцию /А/
==== 2.2. Определение глобальных имен ====
Глобальные имена определяются с помощью инструкций EQU и BLOCK:
<ИМЯ>:EQU <ВЫРАЖЕНИЕ>;
Здесь новое <имя> определяется как число, равное значению <выражения>.
Трансляция глобального контекста производится за один проход, поэтому
все имена, входящие в <выражение>, должны быть уже определены.
<ИМЯ>:BLOCK <СПИСОК ИМЕН>;
Это способ "группового" определения имен, аналогичный фортрановскому
common-блоку. <имя> должно быть уже определено.
Nаме:block nам1,nам2(2),nам3; - эквивалентно
nам1:equ nаме; nам2:equ nам1+1; nам3:equ nам2+2;
В левой части инструкции BLOCK разрешено указывать шестнадцатиричные
числа с префиксом "*":
*1а:block а,в(5),с; - эквивалентно
а:equ $1а; в:equ $1в; с:equ $20;
Шестнадцатиричные числа в правой части инструкции (входящие в качестве
термов в выражения) снабжаются префиксом "$" /доллар/).
==== 2.3. Описание констант из ПЗУ ====
В адресном поле микрокоманды кроме адреса по ОЗУ микропрограмм может
находиться адрес по ПЗУ констант. Для этого в адресном выражении
достаточно указать имя константы. Имена констант являются глобальными,
т.е. Предописанными в любой подпрограмме. Константы размещаются в ПЗУ в
порядке появления их описаний, начиная с адреса 0. Формат описания
константы:
<ИМЯ>:CONST <ШЕСТНАДЦАТИРИЧНОЕ ЧИСЛО>;
Разрядность всех констант одинакова. Она задается в инструкции DEFINE .
Левые нули в константах можно опускать.
==== 2.4. Немного о синтаксисе ====
На одной карте находится одна инструкция. Формат - не фиксированный
инструкция может начинаться с любой позиции карты. Пробелы, вообще
говоря, являются разделителями. Пробелы между разделителем и началом
следующей лексемы игнорируются. Разделители: двоеточие, точка с запятой,
запятая, плюс, минус. Общий формат инструкции:
[ <имя>: ] <код инструкции> [ <параметры> ];
Все символы после ";" до конца текущей карты рассматриваются как
комментарий. В частности, на карте может находиться только комментарий,
если ";" расположена в 1-й позиции. Если после инструкции комментария
нет, символ ";" можно опустить.
Недолгий опыт работы с микроассемблером показал, что очень часто для
размещения инструкции одной карты не хватает. Поэтому принято такое
решение: символ ">", находящийся в первой позиции, означает, что эта
карта является продолжением предыдущей. Ассемблер способен "заглядывать
вперед" по файлу чтения. Обнаружив ">" в первой позиции следующей карты,
он просто "подклеивает ее в хвост" текущей. Число карт продолжения - не
более 20.
==== 2.5. Пример ====
*libra:23,25
*file:scratch,30,w
*call micini
define:prog 72; - разрядность = 72
; по умолчанию разрядность пзу = 64
;
; определим поля микрокоманды:
seqv:field 72,64,/с/; - код микрооперации
а:field 50,63,/а/; - адреса переходов
алу:field 49,40; - к о п а л у
ci:field 39,
>39,1; - пример карты-продолжения
;
; определим коды секвенсора:
jump:equ 0
test:equ jump+1
;
; определим коды а л у:
*0:block and,or,sub,add,mul;
;
; парочка пзу - констант:
к1:const ffff;
к2:const 1234567890abcdef;
;
; и еще несколько глобальных имен:
one:equ 1
two:equ 2-one+1;
ten:equ 9+two-one
end;
*end
===== 3. Трансляция микропрограмм =====
Вся микропрограмма складывается из отдельных блоков - подпрограмм.
Допускается раздельная трансляция подпрограмм. Результатом трансляции
является модуль загрузки, записанный в библиотеку обьектных модулей. При
этом, если в библиотеке уже была программа с таким же именем, то старая
версия уничтожается (аналогично библиотекам в мониторной системе
"дубна"). При трансляции используется уже имеющийся в архиве глобальный
контекст. Перечислим теперь синтаксические конструкции, из которых
складывается подпрограмма:
* 1. Заголовок подпрограммы
<ИМЯ ПОДПРОГРАММЫ>:PROG [ <ХАРАКТЕРИСТИКА> ] ;
характеристика - это шестнадцатиричное число, интерпретацией которого
будет заниматься только загрузчик. На трансляцию программы ее
характеристика не влияет.
* 2. Конец подпрограммы
END;
* 3. Описание точки входа (entry)
<ИМЯ ВХОДА>:ENTRY;
* 4. Описание внешних программ
EXTERN <СПИСОК ИМЕН>;
* 5. Определение локальных имен
<ИМЯ>:EQU <ВЫРАЖЕНИЕ>;
<ИМЯ ИЛИ *<ШЕСТН.чИСЛО>>:BLOCK <СПИСОК ИМЕН>;
эти конструкции "работают" так же, как в 2.2.
Но, в отличие от глобального контекста, здесь значением выражения может
быть не только число, но и адрес. Допускается использование еще не
определенных имен. Переопределение глобальных имен не допускается.
* 6. Изменение начального содержимого поля
<ИМЯ ПОЛЯ>:VALUE <ВЫРАЖЕНИЕ>;
начальным состоянием заданного поля считается значение <выражения> в
правой части инструкции. Инструкция "работает" только в пределах
транслируемой подпрограммы. При переходе к следующей подпрограмме
ассемблер вновь извлекает информацию о начальных значениях полей из
глобального контекста.
* 7. Микрокоманда
[<МЕТКА>:]<КОП> <СПИСОК ПОЛЕЙ И ИХ ЗНАЧЕНИЙ>;
<КОП> - код микрооперации (его еще иногда называют кодом операции
секвенсора). Это символическое имя, определенное в глобальном или
локальном контексте.
Например: *6:block рор,push;
ассемблер подставляет значение имени <КОП> в то поле микрокоманды,
которое помечено маркировкой /С/. (см. 2.1).
Заполнение остальных полей микрокоманды производится по общему
алгоритму, на основании <списка полей> в правой части микрокоманды.
Элементы списка задааются в произвольном порядке и отделяются друг от
друга запятыми. Каждый элемент списка представляется либо парой:
<имя поля> = <выражение>
либо просто именем поля (для однобитовых полей). Если в списке какое-то
поле не указано, то в команду заносится начальное значение этого поля,
указанное инструкцией VALUE или взятое из глобального контекста.
Здесь возможны неоднозначности при наличии вложенных полей. Для
определенности принято следующее решение: незаполненные поля
просматриваются в порядке убывания их ширины (т.е.расстояния между левой
и правой границами). Далее, если поле получило значение, то заполненными
считаются и все вложенные в него поля.
Маркировка адресного поля (см. 2.1) позволяет отслеживать случаи
ошибочного заполнения неадресных полей адресной информацией.
Примеры микрокоманд:
Рассмотрим небольшой фрагмент микропрограммы. При этом мы воспользуемся
глобальным контекстом, описанным в (2.4).
снеск1:test а=снеск2,ci,алу=sub
jump а=*-1
снеск2:jump а=inter,алу=add
Поле SEQV (оно отмечено маркировкой /С/ ) заполняется в первой команде
кодом 1 (test=1), а во второй и третьей - кодом 0 (jump=0).
В поле А попадут соответственно адреса меток СНЕСК2, СНЕСК1 и INTER.
Терм "*" в адресных выражениях, как и в большинстве обычных ассемблеров,
означает текущее состояние счетчика относительных адресов внутри
подпрограммы. Окончательная настройка программы по адресам выполняется
загрузчиком.
В поле АЛУ попадет в первом случае код 2, в третьем - код 3. Для второй
команды "сработает" правило умолчания, и в поле АЛУ попадет код 0.
Поле CI во всех трех командах равно 1. В первой команде оно явно
указано в списке полей (наличие имени однобитового поля в списке влечет
заполнение его кодом 1). В остальных командах оно заполнится "1" по
умолчанию.
Если бы нам требовалось при умолчании полагать CI = 0, то следовало бы
изменить начальное значение этого поля с помощью инструкции value:
CI:VALUE 0;
==== 3.1. Управление печатью листинга ====
Карта *nо list , встретившаяся в любом месте текста программы,
отключает печать листинга. Карта *full list , встретившаяся в любом
месте текста программы, включает печать листинга. Ошибочные карты
распечатываются безусловно.
Листинг программы печатается строками шириной 128 позиций. Строка
соответствует одной инструкции микроассемблера. Формат строки:
Аааа ннн....н llllll:кккк....к
* а - шестнадцатиричный относительный адрес или пусто;
* н - строка обьектного кода в шестнадцатиричном виде. Если в инструкции обнаружена ошибка, то в этом поле появляется текст диагностики.
* L - метка в команде или пусто;
* к - код операции, параметры и комментарий.
Если инструкция не умещается в 128 позиций, производится перенос в
следующую строку листинга, причем поля а-l печатаются пустыми. Перенос
производится на границе элементов списка полей микрокоманды.
При ошибках в программе ошибочный фрагмент отмечается символом "*" в
следующей строке листинга. В большинстве случаев мест0нахождение ошибки
отмечается с точностью до символа, в крайнем случае - с точностью до
лексемы.
==== 3.2. Диагностика ====
* "ошибка в заголовке программы" - нет ":" после имени или нет слова PROG.
* "неверно задана разрядность" - в конструкции define:prog n,к; n или к задано неверно.
* "нет глобального контекста" - в архиве отсутствует описание глобального контекста.
* "не описаны поля микрокоманды" - в глобальном контексте отсутствует описание полей.
* "неверное задание границ поля" - в инструкции FIELD одна из границ поля превышает разрядность команды.
* "надо ровно одно поле /с/" - в глобальном контексте отсутствует или обьявлено дважды поле кода операции секвенсора.
* "семантическая ошибка" - не выполнен один из заданных семантических критериев (об этом см. П. 3.3.).
* "не описан идентификатор" - в инструкции используется несуществующее имя.
* "дважды описано имя" - повторное вхождение имени в левой части инструкции.
* "синтаксическая ошибка" - неверный формат числа, выражения и т.п.
* "переполнение поля" - содержимое поля не вмещается в его границы.
* "наложение с полем" - повторное заполнение одного и того же поля или попытка заполнения сразу обоих вложенных полей.
* "недопустимый вид выражения" - два внешних адреса или сумма относительный адресов.
* "неопознанная инструкция" - как правило, при неуказании кода операции секвенсора.
* "ошибка в идентификаторе" - метка не является идентификатором.
* "нет такого поля" - имя поля отсутствует в глобальном контексте.
* "нет такого коп" - символическое имя кода операции секвенсора отсутствует в глобальном контексте.
* "не адресное поле" - адресная информация попала в неадресное поле.
После текста подпрограммы в листинге печатается таблица использования
меток и внешних имен. Для каждого имени выдается список относительных
адресов программы, где используется это имя. В начале списка находится
относительный адрес метки в программе или символ "е" (для внешних
программ).
==== 3.3. Семантический контроль микрокоманд ====
В общем случае, ассемблер "не ведает" смысла отдельных полей
микрокоманды и логики их взаимодействия. Однако простейшие семантические
правила мы можем ему задать. Правила помещаются перед текстом первой из
транслируемых микропрограмм и "работают" до конца файла. Пока правил
только 2:
* 1. Список возможных значений полей.
Flist <имя поля>=<список>
например, правило: flist тур=тур1,тур2,тур7
означает, что в выражение, задающее значение поля тур , могут входить
термы тур1, тур2, тур7 и только они. Если в какой-то микрокоманде
встретится фрагмент тур=тур4 , то это будет считаться ошибкой.
Если список не умещается во входной строке, оставьте там последнюю
уместившуюся запятую, и продолжайте список с новой строки. Ограничение
на количество правил "flist": суммарная длина всех списков - не более
1500.
* 2. Список ошибочных комбинаций полей.
Снеск field1+field2-field3
этот пример следует читать так: если заданы значения (неважно, какие)
полей field1 и field2, но не задано field3, то такая комбинация полей
в микрокоманде - ошибочна. Всего правил "снеск" может быть не более 200,
в каждом правиле - не более 6 членов.
Диагностика "семантическая ошибка" снабжается номером нарушенного
семантического правила. Кроме того, символ "*" отмечает позицию сканера
входной строки в момент последнего нарушения правила.
==== 3.4. Макросы ====
Часто повторяющиеся фрагменты микрокоманд можно описать единожды в виде
макроопределения. Макроопределения размещаются во входном файле среди
семантических правил (относительный порядок - несуществен) до начала
первой транслируемой программы. Макроопределение занимает ровно одну
строку и выглядит так:
Macro <имя> <заменяющий текст>
имя макроса не более чем из 6 символов. В заменяющем тексте все символы
существенны, кроме хвостовых пробелов. Отсюда следует возможность пустых
макросов, когда в заменяющем тексте нет ни одного символа, отличного от
пробела. Макровызов осуществляется указанием в нужном месте текста
микрокоманды имени макроса, обрамленного символом "%". Пример:
макроопределение: macro м1 f1=с1,f2=с2,f3=1
макровызов: cont f5=5,%м1%,f7
окончательный итог: cont f5=5,f1=с1,f2=с2,f3=1,f7
Всего может быть до 400 макросов. Суммарный обьем заменяющих текстов -
до 24000 символов. Повторное определение макроса не контролируется.
==== 3.5. Псевдокомментарий ====
В системе предусмотрена возможность подготовки карты памяти
микропрограмм (например, для прожига в ПЗУ списка входных точек
программы). Это делается с помощью псевдокомментария
;prom:<список индексов>
например:
;prom:n1,n2-n3,n4-n5,n6
Здесь список шестнадцатиричных чисел задает отдельные индексы или
диапазоны индексов в так называемом переключателе начальных адресов
(ПНА). В соответствующие строки ПНА загрузчик занесет абсолютный
адрес микрокоманды, следующей в тексте программы за псевдокомментарием
;prom:...
Повторное использование одного и того же индекса в ПНА контролируется
загрузчиком. Индексы задаются в диапазоне от 0 до ffff.
===== 4. Архив кросс-системы =====
Архив всегда заказывается в пакете задачи под логическим номером 30 и в
режиме записи. С точки зрения системы архив представляет из себя
виртуальную память прямого доступа. Каждое слово этой памяти адресуется
своим целочисленным адресом = 0,1,2
Распределение виртуальной памяти:
* 0 - код наличия глобального контекста;
* 1 - разрядность микрослова N;
* 2 - ближайшее к N сверху число, кратное 4;
* 3 - длина микрослова в словах бэсм-6;
* 4 - разрядность ПЗУ и количество констант;
* 5 - указатель свободного места в архиве;
* 6 - начало таблицы имен;
* 7 - длина таблицы имен;
* 8 - начало списка констант;
* 20 - таблица имен;
* - список пзу - констант;
* - библиотека модулей загрузки;
Элемент таблицы имен занимает 5 слов. Работа с таблицей производится
методом хеширования, причем в качестве ключа поиска используются
идентификатор и тип обьекта:
* Тип=1 - локальное имя. По окончании трансляции подпрограммы все локальные имена изымаются из таблицы.
* Тип=2 - глобальное имя или описание пзу-константы;
* Тип=3 - описание поля. Глобальные имена и описания полей появляются в таблице при трансляции глобального контекста и живут там во все время существования архива.
* Тип=4 - описание подпрограммы;
* Тип=5 - описание entry-входа.
Таким образом, таблица имен является одновременно и каталогом библиотеки
модулей загрузки. В описании подпрограммы хранится виртуальный адрес
начала ее модуля загрузки в библиотеке и размеры модуля загрузки. Модуль
состоит из двух частей:
* группа команд;
* список адресных ссылок;
Поскольку размеры адресного поля микрокоманды не фиксированы, то
организация модуля загрузки с использованием условных адресов вызывает
затруднения. Вместо этого мы использовали в некотором смысле
противоположный метод. Список адресных ссылок содержит информацию для
загрузчика: в каких командах и каким образом следует модифицировать
адресное поле. Список упорядочен по возрастанию относительных адресов
команд, что позволяет провести настройку адресов за один просмотр модуля
загрузки.
Кроме того, в этом же списке сохраняются и имена локальных меток
программы, переписываемые туда из таблицы имен по окончании трансляции.
Это сделано впрок, для удобства работы с интерпретатором, буде таковой
появится в составе нашей кросс-системы.
Свободная память в архиве используется системой как рабочая область. В
частности, ассемблер использует ее для хранения текста транслируемой
программы (схема трансляции - двухпроходная). Каталог архива может быть
распечатан по директиве
*call miccat
или *call miccat:rом
В последнем случае выдается еще и содержимое п з у констант.
Исключение программы из архива делается с помощью директивы
*call delete:<имя программы>
===== 5. Получение двоичной программы =====
Для получения двоичной программы используется связывающий загрузчик.
Пакет задачи в этом случае выглядит следующим образом:
*nаме .....
*pass:.....
*time:.....
*library:23,25
<заказ архива под номером 30>
*call micloa:<имя головной программы> [=а]
*end file
Разумеется, вызвать загрузчик можно и сразу же по окончании трансляции
текста программы.
Загрузка начинается с заданного шестнадцатиричного адреса А. При
неуказании полагается А = 0. Двоичный образ загруженной программы
помещается в свободную область архива кросс-системы. Оттуда он может
быть выведен, например, на перфоленту. В настоящее время загрузчик,
закончив свою работу, просто распечатывает получаемый код в
шестнадцатиричном виде.
Таблица загрузки печатается в таком же виде, что и таблица загрузки в
мониторной системе "дубна". И точно также, в списке загрузки могут
печататься диагностические сообщения:
* нет программы <имя программы>
* длинный адрес в <адрес команды>
* свободно <адрес>
При загрузке возможны такие требования, как необходимость грузить
некоторые программы с адреса, кратного заданному числу, формировать
карту памяти микропрограмм, да мало ли еще что. Выше мы упоминали т.н.
Характеристику программы - набор шестнадцатиричных цифр, смысл которых
пока не конкретизировался.
В нашем варианте загрузчика используется только младшая цифра
характеристики. Она уточняет начальный адрес загружаемой программы:
* 0 - можно грузить с любого места;
* 1 - только с адреса, кратного 2;
* 2 - только с адреса, кратного 4; и т.д.
Список загрузки формируется в common-блоке
common /linf1/ ь(9),list(3,1000)
1-имя программы, 2-ее характеристика, 3-адрес.
Для связи с системой моделирования ПУЛЬС имеется второй вход в загрузчик:
*call impuls:nр,nа,nо
* nр - имя головной подпрограммы;
* nа - имя архива системы ПУЛЬС;
* nо - имя обьекта в архиве;
Еще один вход в загрузчик сделан для передачи готовой программы в
пультовой процессор МКБ-8601 (пока это iвм рс/хт в к.227):
*call mpsend:<имя головной программы> [=а]
Как уже было сказано выше, результат сборки микропрограммы записывается
загрузчиком в свободную область архива кросс-системы ( ее адрес в 5-м
слове архива ). Собранная программа предваряется переключателем нач.
Адресов ( П Н А ), длиной 16400 слов, сформированным согласно
псевдокомментарию ;prom: (см.3.5).
ПНА можно вывести из архива по карте
*CALL MICPNA:N,ТУР
Где N - номер ПНА = 0,1,2,...,8
ТУР = high или low - определяет, какой байт
выводить: старший (high) или младший (low).