Программирование на БК-0010-01/Глава 5

Материал из Emuverse
Версия от 15:56, 29 ноября 2007; Panther (обсуждение | вклад) (http://www.vak.ru/doku.php/proj/bk/bk5prog)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Данный материал защищён авторскими правами!

Использование материала заявлено как добросовестное, исключительно для образовательных некоммерческих целей.

                         5.1. Отладчик MIRAGE

   Система MIRAGE  С.Зильберштейна  (г.Киров)  - одно из самых удобных
средств,  облегчающих  программирование  в  кодах  на  БК  и   отладку
программы  в кодах.  В комплект поставки входит описание системы и две
версии программы:
   MIRAGE26 -  загружается  с  адреса  26000  и  позволяет  работать с
программами в адресах ОЗУ 1000-25777;
   MIRAGE -  загружается  в  экранное  ОЗУ  с адреса 66000 и позволяет
работать с программами максимальной длины (вплоть до 37777). Платой за
эту  возможность  служит  невозможность отладки программ со спрайтовой
графикой,  поскольку  они  могут  запортить  отладчик,  находящийся  в
экранном ОЗУ.
   Существует также  версия  этого  отладчика,  распространенная   под
названием OS0010F, приспособленная П.Эльтерманом (г.Москва) для работы
с Фокалом (возможность перехода из Фокала в MIRAGE  и  обратно  удобна
при написании и отладке программ типа "Фокод").
   MIRAGE работает в двух основных режимах: командном и экранном.
   При начальном  запуске система находится в командном режиме.  Здесь
можно  давать  команды  работы   с   файлами,   с   памятью,   команды
редактирования  и  трассировки  отлаживаемой  программы.  При описании
команд используются обозначения "начало"  и  "конец"  для  обозначения
начального  и  конечного  адреса,  "имя"  -  имя  файла  при  работе с
магнитофоном. Необязательные элементы заключены в квадратные скобки.
   LOAD имя - загрузка файла с адреса, указанного на ленте;
   LOAD имя/адрес - загрузка с указанного адреса;
   SAVE имя/начало/конец - запись программы на магнитофон;
   SAVE - запись программы на магнитофон с тем же именем,  начальным и
конечным  адресом,  что  были  указаны в последней выполненной команде
SAVE;
   SET имя - поиск файла на ленте;
   DIR - просмотр названий файлов на ленте;
   D адрес - пошаговый дамп содержимого ОЗУ с  указанного  адреса  по
словам (распечатка в восьмеричной системе счисления);
   D адрес/B - то же по байтам.
В режиме дампа действуют команды: "ВК" -  продолжить  дальше,  "."  -
окончить дамп и выйти в командный режим.
   DR - распечатка содержимого регистров  процессора  R0-R5,  SP,  PC
и признаков PSW N,Z,V,C;
   S адрес -  пошаговый  просмотр  и  изменение  содержимого  памяти,
начиная с указанного адреса по словам;
   S адрес/B - то же по байтам;
   SR - пошаговый просмотр и изменение регистров процессора.
   В этих  режимах  можно,   нажимая   "ВК"   или   "стрелку   вверх",
просматривать  содержимое  памяти  соответственно в сторону увеличения
либо уменьшения адресов, а при необходимости внести изменения: набрать
новое значение слова (байта) в восьмеричной системе счисления и нажать
"ВК" или "стрелку вверх".  "." - конец пошагового просмотра и выход  в
командный  режим.  В  режиме  просмотра  по  байтам  "S адрес/B" можно
вводить строку символов, начинающуюся кавычками.
   F начало/конец/код  -  заполнение  указанного участка ОЗУ значением
"код";
   M начало/конец/адрес - побайтное копирование участка ОЗУ, заданного
начальным и конечным адресом, в область, начиная с указанного адреса;
   C начало/конец/адрес   -  пословное  сравнение  двух  участков  ОЗУ
одинаковой длины:  один задан начальным  и  конечным  адресом,  второй
начинается с указанного адреса;
   W начало/конец/слово[/маска] - поиск "слова"  в  указанном  участке
ОЗУ.  Если  задана  "маска",  то  ведется  поиск  всех  слов,  которые
совпадают с образцом поиска ("словом") только  в  тех  битах,  которые
установлены в маске. Например:
   W 1000/2000/123 - найти и указать адреса слов в диапазоне  от  1000
до 2000, в которых записано значение 123;
   W 1000/2000/177600/177600  -  найти  в  указанном   диапазоне   все
обращения к системным регистрам, то есть слова, значения которых лежат
в диапазоне от 177600 до 177777.  Учтите при этом, что MIRAGE не может
в подобных случаях отличить адрес (то,  что нам и надо) от данных (что
мы вовсе не имели в виду,  давая эту  команду).  В  каждом  конкретном
случае разбираться приходится самому программисту.
   W начало/конец/адрес[/маска]/R  -  поиск  относительных  ссылок  на
указанный адрес (или интервал адресов, определенный маской). Например,
у нас есть кусок программы:

     1000:        167        JMP        4000
     1002:        2774

где применена  относительная  адресация  в  команде перехода.  Если мы
дадим в MIRAGE команду "W 1000/1010/4000/R", то получим сообщение:

     001002:        004000

из которого ясно,  что по адресу 1002 найдена относительная ссылка  на
адрес 4000.
   U начало/конец - подсчитать контрольную сумму в указанном участке.
   G [начало][/конец]  -  запуск  программы с начального адреса (или с
текущего содержимого  PC,  если  "начало"  не  указано)  до  конечного
адреса.  Если "конец" не указан, то управление системе MIRAGE вернется
по команде HALT в отлаживаемой программе  либо  по  прерыванию  ее  по
ошибке или по клавише "СТОП".
   T [адрес] - пошаговая трассировка  программы  с  указанного  адреса
(либо   с   текущего   содержимого   PC).   В   процессе   трассировки
распечатываются текущие значения регистров R0-R5,  SP,  PC и  признаки
PSW N,Z,V,C, затем очередная команда, на которую показывает PC. "ВК" -
продолжение  трассировки,  "."  или  "СТОП"  -  окончание  и  выход  в
командный режим.
   R адрес - вызов подпрограммы с указанного адреса. MIRAGE вернется в
командный режим по достижении команды возврата из подпрограммы RTS PC.
   A начало[/конец] - перейти к экранному редактированию  программы  в
указанном диапазоне адресов;  при этом на экране отображаются адреса и
мнемоника команд;
   ASM начало[/конец] - перейти к экранному редактированию программы в
указанном диапазоне адресов;  при этом на экране отображаются  адреса,
коды и мнемоника команд.
   В режиме экранного редактирования MIRAGE дизассемблирует часть  ОЗУ
(то  есть  переводит коды команд в их ассемблерные мнемоники).  Если в
этом участке встречаются данные,  то они также считаются  командами  и
будут  дизассемблированы.  Естественно,  есть  такие коды,  которые не
соответствуют ни одной  команде  процессора;  в  этом  случае  пишется
мнемоника  WORD  (слово) и содержимое этого слова.  По обилию мнемоник
WORD среди прочих правдоподобно выглядящих ассемблерных  команд  можно
распознать массивы данных в программе.
   MIRAGE для удобства программиста заменяет команды "JSR PC,адрес" на
"CALL  адрес"  и "RTS PC" на "RET";  а для удобства ввода кода символа
позволяет писать команды вида "MOV #'A,R0",  что тут  же  переведет  в
"MOV #101,R0".
   В режиме экранного  редактирования  можно  редактировать  мнемоники
команд  с  помощью клавиш управления курсором;  все сделанные в данной
строке изменения будут приняты только после  нажатия  "ВК"  в  момент,
когда курсор стоит в этой строке. Редактор работает только в режиме 64
символа в строке;  нельзя также допускать выхода  курсора  за  пределы
экрана !
   В режиме экранного редактирования действуют клавиши:
   "КТ" - листание текста на экран вперед;
   "СУ/Т" - листание на один экран назад (только после "КТ");
   "ШАГ" -  возврат  к  первому листу (с адреса,  указанного в команде
ASM);
   "ВС" -  вставка  команды  NOP  под курсором (нужно всегда учитывать
размер команды,  которую Вы желаете вставить,  и вставлять  для  этого
столько NOP-ов,  сколько слов содержит команда. Об этом надо помнить и
при редактировании старой команды);
   "СБР" - удалить команду под курсором;
   "СТОП" - выход в командный режим.
   Рассмотрим пример  работы  с  системой  MIRAGE.  Попробуем  набрать
программу буквально из нескольких команд. Загрузим MIRAGE26 и запустим
его. Очистим  командой  "F  1000/2000/0"  участок ОЗУ с адреса 1000 до
адреса 2000 и будем здесь набирать нашу программу.  Для этого перейдем
в режим экранного редактирования командой "ASM 1000" и увидим:

     1000:        000000                HALT
     1002:        000000                HALT
     1004:         . . .

   Это - наша,  еще пустая, рабочая область. Никакого труда, вероятно,
не составит набрать такую короткую программу (нажимая "ВК" после ввода
каждой команды):

     1000:        010100                MOV     R1,R0
     1002:        104016                EMT     16
     1004:        000775                BR      1000

   Предположим, мы  хотим  заменить  первую  команду  на  команду "MOV
#101,R0".  Учитывая,  что эта команда займет  два  слова,  а  в  нашем
распоряжении  только  одно,  надо  поставить курсор на вторую строку и
клавишей "ВС" вставить пустую команду. Программа приобретет вид:

     1000:        010100                MOV     R1,R0
     1002:        000240                NOP
     1004:        104016                EMT     16
     1006:        000775                BR      1002

   Теперь подведем  курсор  к первой строке,  заменим "R1" на "#101" и
нажмем "ВК". Программа примет вид:

     1000:        010100  000101        MOV     #101,R0
     1004:        104016                EMT     16
     1006:        000775                BR      1002

   Мы видим,  что команда "MOV #101,R0"  заняла  два  слова  памяти  и
"съела" при этом вставленную команду "NOP". Если бы мы ее не вставили,
пропала бы следующая команда "EMT 16".
   Теперь осталось  внести  последнее исправление.  В команде перехода
использована относительная адресация,  и, сдвинутая со старого места в
ОЗУ,  команда  перехода  показывает  уже не на тот адрес.  Поэтому при
вставке или удалении команд приходится вручную корректировать адреса в
сдвинутых командах перехода. Это самое большое неудобство при работе с
подобными отладчиками.  Можно посоветовать всегда  иметь  перед  собой
текст  программы  на  бумаге  и  в  процессе  работы  вносить туда все
необходимые изменения.  Нужно также иметь отдельную таблицу, в которой
перечислены   адреса   используемых   Вами  подпрограмм  и  глобальных
переменных. Такая таблица очень поможет Вам в работе.
   Еще один  полезный  совет  заключается  в  правильном использовании
режимов адресации.  Отлаживая программу по  частям  или  по  отдельным
подпрограммам,  старайтесь,  чтобы  все  переходы внутри этого кусочка
использовали  относительную  адресацию.  Тогда  после  отладки  данной
подпрограммы  ее  можно  будет  перемещать  в ОЗУ,  заботясь только об
изменении ссылок на глобальные  имена  -  на  внешние  подпрограммы  и
переменные, которые к данному кусочку программы не привязаны.
   Теперь давайте попробуем запустить нашу программу. Для этого нажмем
"СТОП" -  MIRAGE26  перейдет  в  командный  режим.  Запустим программу
командой "G 1000".  И она заработает - начнет выводить на экран  букву
"А"  (это ее код - 101).  Чтобы остановить нашу зацикленную программу,
нажмем "СТОП" и вновь окажемся в командном режиме.

                       5.2. Отладчики типа ГРОТ

   Распространено много версий отладчиков под разными названиями (ОТЛ,
ОТЛАДЧИК3,  ГРОТ и др.), которые по существу представляют собой одно и
то же.  Коротко опишем версию,  в командной строке которой  появляется
надпись "Микро-отладчик <V1.1 860702 Москва-Рига>".
   По своему  принципу  действия  этот  отладчик  аналогичен   системе
MIRAGE,  но  имеет  несколько  меньшие  возможности  и  менее удобен в
работе.  Он  не имеет  экранного редактора  и удобной функции вставки-
удаления команд.  На экране его отображается адрес, мнемоника команды,
ее коды по словам и по байтам,  а также содержимое байтов в символьном
виде:

1000        MOV        #101,R0         012700        25.300        Ю
                                       000101        0.101        А
1004        EMT        16              104016        210.16
1006        HALT                       000000        0.0

   В правом  верхнем  углу  выводятся   текущие   значения   регистров
процессора и признаков N,Z,V,C.
   Команды отладчика:
   адресА - установить текущее значение адреса (как в ТС-отладчике);
   К - выход в пусковой монитор;
   , (запятая) - перемещение текущего адреса на команду вперед;
   - (минус) - перемещение текущего адреса на команду назад;
   I - вывести мнемонику команды по текущему адресу;
   "ВК" - ввести мнемонику команды и записать ее по текущему адресу;
   nD - сделать дамп n байт памяти с текущего адреса;
   nL - распечатать n команд с текущего адреса;
   конецPадрес -  скопировать область ОЗУ с текущего по конечный адрес
в область, начинающуюся с указанного адреса;
   R - вывод регистров процессора;
   значениеRn - записать "значение" в регистр Rn;
   О - очистить рабочие переменные отладчика;
   адресG -  запустить  программу  с  указанного  адреса;   управление
вернется  отладчику по команде HALT или другому аварийному прерыванию,
либо по установленной контрольной точке;
   адресТ - установить контрольную точку - адрес команды, где
программа должна остановиться при отладке;
   T - снятие контрольной точки;
   адрес[ - пошаговая трассировка программы;
   адресY - медленная автоматическая трассировка (1 команды в 5 сек.);
   адрес] - ускоренная автоматическая трассировка (1 команда в сек.).
   Если в  этих  командах  не указать адрес,  то выполнение начнется с
команды, на которую указывает PC.
   адресЧ - чтение программы с магнитофона;
   адресЗдлина - запись программы на магнитофон.

                        5.3. Ассемблер МИКРО

                        5.3.1. Описание языка

   Работа с   помощью   описанных   отладчиков    очень    близка    к
программированию в машинных кодах. Отладчики представляют собой первую
ступень механизации программирования в кодах - замену  кодов  машинных
команд  удобными для человека их мнемоническими обозначениями.  Вся же
работа по распределению памяти,  учету адресов  лежит  по-прежнему  на
программисте.
   Второй ступенью механизации программирования является  программиро-
вание  на  языке низкого (машинного) уровня - языке ассемблера.  Как и
система команд,  свой язык ассемблера характерен для каждого типа ЭВМ.
Что же принципиально новое дает программисту язык ассемблера ?  Прежде
всего,  он позволяет использовать  метки  в  тексте  программы.  Метки
используются  для указания адреса в операциях перехода и пересылки.  В
этом отношении они похожи на номера строк Бейсика или  Фокала.  Теперь
программист избавлен от необходимости учета физических адресов памяти,
он работает только  с  метками,  а  адрес  получается  при  трансляции
ассемблерной программы. Если Вы добавите или удалите несколько команд,
то Вам не придется корректировать адреса в сдвинутых с места  командах
перехода. Просто текст программы надо оттранслировать заново.
   Для перевода  программы  на  языке  ассемблера  в   машинные   коды
используется  специальная программа,  называемая ассемблером.  Процесс
перевода называется ассемблированием.  На  ЭВМ  ДВК,  СМ  используется
мощный   ассемблер  MACRO,  имеющий  очень  много  облегчающих  работу
программиста средств (макрокоманды,  условная трансляция и др.). На БК
используются  ассемблеры попроще.  Наибольшее распространение получила
система   МИКРО,    разработанная    С.В.Шмытовым,    А.Н.Сомовым    и
С.А.Кумандиным  (г.Москва).  Ассемблер  МИКРО включает в себя редактор
текста,  транслятор  и  компоновщик,  которые   объединяет   командный
монитор.  Распространено  несколько версий системы:  МИКРО8С,  МИКРО9,
МИКРО10,  МИКРО11.  Особенности  каждой  новой  версии  отражаются   в
соответствующем описании. Здесь приведены основные сведения по МИКРО9.
Основные характеристики системы следующие:
   - объем ОЗУ, занимаемый системой - 4.5К;
   - максимальный объем исходного текста - 8К;
   - максимальный размер транслируемой за один раз программы - 2К;
   - максимальный размер программы, полученной при компоновке в
режиме РП - 16.5К.
   Работа с системой будет описана ниже,  а пока познакомимся с языком
ассемблера БК и его особенностями в системе МИКРО.
   Программа на языке ассемблера,  как и программа на Бейсике, состоит
из строк (операторов). В одной строке может быть записана одна команда
в обычной ассемблерной мнемонике, либо комментарий.
   Строка программы  (оператор)  может состоять из 4-х частей (полей).
На рис.24 приведен пример оператора на языке ассемблера с обозначением
полей.
                TT:      MOV   R1,R4        ;Пересылка
               └─┬─┘   └─┬──┘ └────┬┘      └─┬────────┘
                Метка   Операция  Операнды  Комментарий
            Рис. 24. Формат оператора на языке ассемблера

   Поля "метка"  и  "комментарий"  не  являются  обязательными частями
оператора.
   Имя метки  состоит не более,  чем из трех символов (букв латинского
алфавита и цифр) и заканчивается  двоеточием.  В  данном  примере  имя
метки состоит из двух букв "ТТ". Меткой обычно "метится" тот оператор,
к которому есть передача управления из  какого-либо  места  программы.
Для  хранения  значений  переменных  используются  определенные  слова
памяти,  которые также обычно "метятся" метками - тогда  каждая  метка
является именем соответствующей переменной.  Если имя метки начинается
с цифры, то метка называется локальной, и ее действие распространяется
только  между  обычными  метками,  поэтому  имена  локальных  меток  в
программе могут повторяться. Двух одинаковых обычных меток в программе
быть не должно.
   Комментарии должны начинаться с символа ";",  их можно  располагать
на отдельной строке или в конце строки оператора.
   Поле "операция"   содержит   мнемокод    машинной    команды    или
псевдокоманду ассемблера МИКРО.
   Псевдокоманды начинаются  с  символа  "."  (точка).  В  таблице  18
рассмотрены основные псевдокоманды, применяемые в ассемблере МИКРО9.
   В конце  текста программы на ассемблере должна стоять псевдокоманда
"END".
                Таблица 18. Основные псевдокоманды ассемблера МИКРО9
┌────────┬──────────────────────────────────────────┬───────────────┐
│ формат │ выполняемые действия                     │   пример      │
╞════════╪══════════════════════════════════════════╪═══════════════╡
│  .+К   │ резервируется К байт                     │   .+45        │
├────────┼──────────────────────────────────────────┼───────────────┤
│  .#Е   │ запись значения выражения Е (в слово)    │   .#14        │
├────────┼──────────────────────────────────────────┼───────────────┤
│  .@E   │ получение смещения. По адресу этой псе-  │   .@MET       │
│        │ вдокоманды записывается число, равное    │               │
│        │ разности адреса метки Е (Е - имя метки)  │               │
│        │ и адреса самой псевдокоманды             │               │
├────────┼──────────────────────────────────────────┼───────────────┤
│   .Е   │ здесь Е - символ "Е". Эта псевдокоманда  │               │
│        │ обнуляет байт (если значение счетчика    │               │
│        │ адресов нечетно) или слово (если значение│               │
│        │ счетчика адресов четно)                  │               │
├────────┼──────────────────────────────────────────┼───────────────┤
│  .В:К  │ Записывает значение выражения К          │   .В:57       │
│        │ в текущий байт                           │               │
├────────┼──────────────────────────────────────────┼───────────────┤
│ .А:... │ Запись произвольной строки символов "..."│ .А:Привет     │
│        │ с текущего адреса                        │               │
└────────┴──────────────────────────────────────────┴───────────────┘
   Поле "операнды" содержит информацию  о  местонахождении  операндов,
участвующих в операции.
   Поле "операция"  отделяется  от  поля  "операнды"  хотя  бы   одним
пробелом. Если команда двухоперандная, то операнд источника отделяется
от операнда приемника запятой. Например:

                           MOV R1,R4

   Ниже будут рассмотрены некоторые характерные для ассемблера примеры
программирования.

Пример 1:    LF=12
             KBD=177662
             MOV #LF,R3                MOV #12,R3
             MOV KBD,R0                MOV @#177662,R0

   Программы слева и справа дадут одинаковый результат.
   Строка программы,   имеющая   вид   <имя> = <число>,     называется
оператором прямого  присваивания.  Этот  оператор  похож  на  описание
констант в языках высокого уровня, и вводится для удобства обозначения
часто используемых констант или адресов (например,  адресов  системных
регистров,  как  в  данном примере).  В данной версии ассемблера МИКРО
есть ограничение:  с помощью  оператора  присваивания  можно  работать
только  с адресами в диапазоне от 0 до 777 и от 100000 до 177777,  при
использовании таких имен в программе будет обеспечен абсолютный  метод
адресации. Этим МИКРО отличается от ассемблера MACRO для ЭВМ ДВК и СМ,
где последнюю команду для получения абсолютной адресации следовало  бы
записать как "MOV @#KBD,R0".

Пример 2:
           Адрес        Команда        Текст на ассемблере
            6000:       000167              JMP VAR+2
            6002:       001004
             ...         ...                 ...
            7006:       005000         VAR: CLR R0
            7010:       010203              MOV R2,R3

   Команда JMP передает управление на адрес, равный значению выражения
VAR+2=7006+2=7010.
   Выражение в  МИКРО9  -  это  имя  метки   и   восьмеричное   число,
соединенные  знаком "+" или "-".  В частном случае выражение - это имя
метки или восьмеричное число.

Пример 3:
           Адрес        Команда        Текст на ассемблере
            1000:       012700              MOV  #6,R0
            1002:       000006
            1004:       005060         A:   CLRB TAB-1(R0)
            1006:       001777
            1010:       077003              SOB  R0,A
                                             . . .
            2000:                      TAB: .+6

   В этом примере  описан  массив  данных  размером  6  байт,  который
размещается с  адреса 2000.  Программа очищает (заполняет нулями) этот
масссив. Для работы с массивом используется индексный метод адресации.
Поскольку индекс  -  R0  меняется  от  6  до  1,  в  качестве смещения
используется   выражение   "TAB-1";   при   значении   R0=1    команда
"CLRB TAB-1(R0)" очистит байт по адресу 2000).

Пример 4:
           Адрес        Команда        Текст на ассемблере
            1000:       010701              MOV  PC,R1
            1002:       062701              ADD  (PC)+,R1
            1004:       000012              .@T+2
            1006:       005002              CLR  R2
            1010:       104020              EMT  20
            1012:       000000              HALT
            1014:                      T:   .A:Привет !
                                            .E

   Этот пример  показывает,  как  на  МИКРО  можно писать перемещаемые
программы. Коды этой программы можно записать с любого адреса  ОЗУ,  и
она везде будет работать одинаково, так как адрес выводимой на дисплей
строки "Привет !" вычисляется относительно текущего значения PC  после
выполнения команды  "MOV  PC,R1":  1002  +  12 = 1014,  где .@T = 10 -
смещение от адреса этой псевдокоманды до начала текста  (метки  Т),  а
еще 2 добавлено для учета самой команды "ADD (PC)+,R1".
   А вот так предлагается организовать подпрограмму для вывода  текста
в описании   МИКРО#10S   (программа  также  перемещаемая).  Предлагаем
читателю самостоятельно разобраться в ее работе,  напомним только, что
после вызова  подпрограммы  командой  "JSR  PC,..."  в  стеке (по @SP)
сохраняется адрес следующей команды (адрес возврата):

        JSR  PC,TEX  ;вызов подпрограммы вывода текста и передача ей
        .@T            ;смещения до текста в качестве параметра
        HALT         ;продолжение основной программы
   T:   .A:Привет !  ;строка текста
        .E           ;нулевой байт в конце строки и выравнивание
                       ;адреса на границу слова
   TEX: MOV  @SP,R1  ;получение адреса псевдокоманды .@T
        ADD  @R1,R1  ;получение абсолютного адреса строки Т
        CLR  R2      ;вывод строки на дисплей
        EMT  20        ;
        ADD  #2,@SP  ;установить адрес возврата на следующую за .@T
        RTS  PC        ;команду программы (в данном случае - HALT)

                    5.3.2. Работа с системой МИКРО

   Для получения  готовой  программы  в  машинных   кодах   необходимо
проделать следующие операции:
 - набор исходного текста (текста программы на языке  ассемблера)  при
помощи редактора текста;
 - трансляция (перевод) исходного  текста  программы  в  промежуточную
заготовку (объектный модуль) при помощи ассемблера (транслятора);
 - компоновка (составление) из нескольких объектных модулей готовой  к
выполнению  программы  в  машинных  кодах  (загрузочного  модуля ) при
помощи программы, называемой компоновщиком.
   Все три   программы:   редактор  текста,  ассемблер  и  компоновщик
соеденены в одной программе МИКРО9,  которая загружается и запускается
на  выполнение с адреса 1000.  Программа МИКРО9 может работать в одном
из четырех режимов:
        - редактирование текста программы;
        - трансляция (ассемблирование);
        - компоновка;
        - командный режим.
   Сразу же  после  запуска МИКРО9 работает в командном режиме,  о чем
свидетельствует символ "*".  В командном режиме возможна подача  одной
из следующих команд:
   LO - Загрузка исходного текста с магнитной ленты (МЛ);
   LF - Подстыковка текста с МЛ к тексту, находящемуся в памяти;
   ST - Запись текста на МЛ;
   SF - Запись на МЛ части текста от начала до строки, в которой
        находился курсор при выходе из редактора текста;
   SS - Запись на МЛ части текста от строки, в которой находился
        курсор при выходе из редактора текста, до конца текста;
   CO - трансляция текста с выводом его на экран;
   CN - трансляция текста без вывода его на экран;
            Примечание: после подачи команд CO и CN на экран
                        выводится вопрос "Е/N?", ответ на который
                        служит заданием действий, выполняемых
                        программой после возникновения ошибки:
                           E - прекратить трансляцию,
                           N - продолжить трансляцию;
   SC - вход в редактор текста;
   SA - запись на МЛ загрузочного модуля (готовой программы);
   SL - запись на МЛ объектного модуля (промежуточной программы);
   LI - компоновка объектного модуля, записанного на МЛ к модулю,
        находящемуся в памяти (при этом получится программа,
        загружаемая с адреса 1000 );
   LA - компоновка объектных модулей, записанных на МЛ, с указанием
        адреса последующей загрузки (эта команда применяется только
        для головного модуля, остальные модули компонуются с помощью
        команды "LI");
   LL - компоновка объектного модуля сразу после трансляции (эта
        команда применяется для программ, состоящих из одного
        модуля; при этом получится программа, загружаемая с адреса
        1000);
   LS - компоновка объектного модуля сразу после трансляции, с
        указанием адреса последующей загрузки.
   В некоторых   версиях   МИКРО   предусмотрена    команда    запуска
скомпонованной программы "RU" или "GO".

                  5.3.2.1. Работа в редакторе текста

   Для набора текста программы на языке ассемблера в редакторе  текста
используются следующие клавиши:
        - клавиши перемещения курсора;
        - клавиши "сдвижка в строке" и "раздвижка в строке";
        - клавиша удаления части строки, расположенной справа от
          курсора;
        - "СУ/Т" - раздвижка строк программы;
        - "ВС" - сдвижка строк программы;
        - "СБР" - переход в конец текста;
        - "СУ/Э" - переход в начало текста;
        - "забой" - удаление символа, стоящего перед курсором;
        - "СУ/Ъ" - выход из редактора;
        - "КТ" - переход к строке с задаваемым номером.
   Клавиши и   особенности   редакторов   в  различных  версиях  МИКРО
отражаются в соответствующих описаниях.
   После изменения  текста  строки,  а  также  для  ввода новой строки
необходимо нажать клавишу "ВК".
   В процессе  редактирования в левом верхнем углу экрана отображается
количество оставшихся байт памяти, отведенных для текста.

                       5.3.2.2. Ассемблирование

   Если в исходном тексте программы на языке ассемблера нет ошибок, то
в  результате трансляции образуется объектный модуль,  иначе ассемблер
выдает сообщение об ошибках в виде:

        ОШИБКА NN В СТР. ММММММ
где  NN     - номер ошибки;
     ММММММ - восьмеричный номер строки текста.
   Ниже приведены номера ошибок и их описание:
     1  - недопустимый символ в строке;
     2  - неверный оператор;
     3  - ошибка длины перехода по команде BR;
     4  - недопустимое имя метки;
     5  - недопустимый символ в поле операнда;
     6  - неверное число операндов;
     7  - нестандартное имя (там, где это необходимо);
     10 - неопределенное имя метки в операторе "SOB";
     11 - неверная псевдокоманда;
     12 - ошибка индексной адресации;
     20 - неправильная команда в командном режиме.

               5.3.2.3. Компоновка загрузочного модуля

   Если исходный   текст  программы  на  языке  ассемблера  невозможно
набрать в один прием, то он набирается частями. Каждая часть исходного
текста  при трансляции образует отдельный объектный модуль,  затем все
объектные модули  связываются  (компонуются)  компоновщиком   в   один
загрузочный   модуль  -  в  программу  в  машинных  кодах,  готовую  к
выполнению.
   Если загрузочный модуль компонуется из одного объектного модуля, то
компоновку можно произвести сразу же после трансляции исходного текста
подачей одной из следующих команд: LL или LS.
   После успешной трансляции распечатывается таблица использованных  в
модуле меток, при этом в инверсном виде распечатываются метки, которые
не определены в данном модуле,  но используются в командах  программы.
Это могут быть глобальные метки (определенные в другом модуле);  тогда
необходима компоновка программы.  Если Ваша программа состоит всего из
одного модуля,    появление    инверсных   меток   в   таблице   будет
свидетельствовать о Ваших опечатках.
   Для компоновки  нескольких  модулей  с  магнитной  ленты  программу
МИКРО9 нужно привести в исходное состояние командой "RS".

              5.3.3. Пример программ на языке ассемблера

   В первом примере приведен  простейший  генератор  случайных  чисел,
который   можно  использовать  в  игровых  программах.  Для  начальной
установки  генератора  случайных  чисел  используется   такой   прием:
программа  крутится  в  цикле,  вызывая  каждый  раз  генерацию нового
случайного числа,  до тех  пор,  пока  пользователь  не  нажмет  любую
клавишу.  Поскольку  подгадать  нажатие  клавиши под нужное количество
циклов практически невозможно,  то работа программы каждый  раз  будет
начинаться  при новом значении случайного числа.  Во время прохождения
очередного цикла можно производить какие-либо действия  на  экране  (в
данном примере случайным образом рисуются точки случайного цвета).

; Пример генератора случайных чисел
        EMT       14      ;очищаем экран;
        MOV       #INE,R1 ;приведем экран в порядок;
        CLR       R2        ;
        EMT       20        ;
        MOV       #100,@#177660  ;запретим прерывания от клавиатуры;
        TST       @#177662;снять готовность, если клавиша была нажата;
; Начальная установка генератора случайных чисел
ING:    MOV       #2,R0   ;получаем случайный цвет (число от 0 до 2);
        JSR       PC,NRN
        ADD       #221,R0 ;получаем код символа установки цвета;
        EMT       16      ;устанавливаем цвет;
        MOV       #377,R0 ;получаем случайное число от 0 до 377
        JSR       PC,NRN    ;и используем его в качестве
        MOV       R0,R1     ;координаты X для вывода точки;
        MOV       #357,R0 ;получаем случайное число от 0 до 357
        JSR       PC,NRN    ;и используем его в качестве
        MOV       R0,R2     ;координаты Y для вывода точки;
        MOV       #1,R0   ;рисуем точку;
        EMT       30        ;
        TSTB      @#177660;если клавиша не нажата,
        BPL       ING       ;продолжаем цикл;
        TST       @#177662;снять запрос на прерывание;
        CLR       @#177660;разрешить прерывания;
        MOV       RAN,R0  ;напечатать полученное
        JSR       PC,OUT    ;случайное число;
        HALT              ;далее можно продолжать программу
;----------------------------------------------------
; Строка инициализации экрана (ставится режим 32 символа в строке,
; убирается курсор и служебная строка)
INE:    .B:233.B:232.B:224.B:236.B:221.B:0
;----------------------------------------------------
; Подпрограмма получения случайного целого числа
FRN:        .#000327      ;это код команды "SWAB #RAN"
RAN:        .#0           ;здесь будет целое случайное число
        INCB      RAN     ;все эти операторы
        ROLB      FRN+3      ;предназначены
MM:     ADD       #0,RAN     ;для получения
        ADD       #3337,MM+2 ;псевдослучайного числа
        RTS       PC
;----------------------------------------------------
; Получение в R0 случайного числа в диапазоне 0..R0
NRN:    JSR       PC,FRN  ;получаем новое случайное число;
        MOV       R1,-(SP)   ;сохраняем регистры в стеке;
        MOV       R2,-(SP)
        MOV       RAN,R1  ;отсекаем старшие разряды
        MOV       #100000,R2 ;случайного числа,
1А:     BIC       R2,R1      ;
        ASR       R2         ;
        CMP       R1,R0      ;пока оно не войдет в заданный
        BHI       1А         ;диапазон;
        MOV       R1,R0   ;результат в R0;
        MOV       (SP)+,R2;восстанавливаем регистры;
        MOV       (SP)+,R1
        RTS       PC
;----------------------------------------------------
;Подпрограмма печати содержимого R0 в десятичной форме
OUT:    TST       R0       ;если число отрицательное,
        BPL       1А         ;то
        NEG       R0         ;изменить его знак на "+",
        MOV       R0,-(SP)   ;сохранить R0 в стеке;
        MOV       #55,R0     ;вывести знак "-"
        EMT       16           ;на экран;
        MOV       (SP)+,R0   ;восстановить из стека R0
1А:     JSR       PC,OU1   ;вывести само число
        RTS       PC
; Рекурсивная подпрограмма печати R0 в десятичной системе счисления
OU1:    MOV       R0,-(SP) ;делим в цикле содержимое стека
        DEC       R1         ;на 10 и получаем там количество единиц
        CLR       R0         ;(то есть младший десятичный разряд
1А:     INC       R0         ;исходного содержимого R0),
        SUB       #12,@SP    ;а в R0 получаем количество десятков
        BGE       1А         ;в исходном числе.
        ADD       #12,@SP    ;
        DEC       R0         ;
        BEQ       2А       ;пока еще остаются в числе десятки
        JSR       PC,OU1   ;(т.е. старшие разряды), снова печатаем их,
                             ;для чего выполняем рекурсивный вызов
                             ;подпрограммы OU1
2А:     MOV       (SP)+,R0 ;когда все число переведено и все
                             ;разряды сохранены в стеке,
                             ;вытаскиваем из стека очередной
        ADD       #60,R0     ;разряд и печатаем соответствующую
        EMT       16         ;цифру
        RTS       PC
        END

   В этом примере для получения случайного числа используется  простая
подпрограмма FRN. Псевдослучайная последовательность чисел, получаемая
с  помощью  этой   подпрограммы,   образует   достаточно   равномерное
распределение.   Но   все   же   определенная  закономерность  в  этой
последовательности ощущается ("случайные" точки на экране более плотно
располагаются  вдоль нескольких прямых).  Случайное 16-разрядное число
генерируется в ячейке с адресом (меткой) RAN.
   Для получения  числа в заданном диапазоне используется подпрограмма
NRN.  Сначала в R0 нужно записать  верхнюю  границу  диапазона,  потом
вызвать  NRN,  и в R0 будет находиться случайное положительное число в
из заданного диапазона.
   Очень интересна подпрограмма печати числа в десятичной форме. Здесь
используется особый  прием  программирования  -  рекурсия.  С  помощью
рекурсивных процедур (то есть подпрограмм, которые вызывают сами себя)
можно вычислять факториал (классический  пример  из  всех  учебников),
закрашивать   участки  экрана  и  многое  другое.  В  Фокале  рекурсия
допустима, в Бейсике - нет.
   Подпрограмма OU1  сохраняет  в стеке количество единиц в печатаемом
числе,  вызывает саму себя,  чтобы напечатать старшие  разряды  числа,
после  чего  берет  положенное  в  стек  количество  единиц и печатает
соответствующую   цифру.   Эта   подпрограмма   является    достаточно
универсальной  -  Вы  можете задать любую систему счисления,  для чего
вместо константы 12 (10 в десятичной системе)  записать  любое  другое
число, и программа будет печатать число в заданной системе счисления.

   Следующий пример  показывает,  как  можно сделать вывод изображения
(спрайта) на экран и управлять его движением.
   Спрайтом называется  прямоугольное изображение,  его размеры обычно
кратны  знакоместу  экрана.   Для   редактирования   спрайтов   удобно
использовать   графические   редакторы,   например,  ГРЕД4  (О.Туйкин,
Д.Баранов).  Этот редактор  позволяет  рисовать  изображения  цветными
точками и записывать их на магнитофон в виде файла следующего формата:
   ширина спрайта в байтах (одно слово);
   высота спрайта в точечных строках (одно слово);
   далее по строкам пишется содержимое спрайта.
   Предположим, мы нарисовали человечка (рис.25). Размер этого спрайта
совпадает с размером одного широкого знакоместа: 2 байта в ширину и 12
точечных строк в высоту (числа восьмеричные).  Соответственно,  ширина
спрайта в цветном  режиме  составляет  8  цветных  точек.  На  рисунке
буквами "К" обозначены красные точки, "З" - зеленые.

                           . . З З К . . .
                           . . . К К . . .
                           . . . К . . . К
                           . К К К К К К .
                           К . . К К . . .
                           . . . К К . . .
                           . . К . . К . .
                           . . К . . К . .
                           . К К . . К К .

                   Рис.25. Пример рисования спрайта

   Если теперь  записать  этот спрайт на магнитофон,  то в файле будет
содержаться последовательность слов:
  2,12,1640,1700,140300,37774,1703,1700,1700,6060,6060,36074,
где первые два слова - ширина спрайта в байтах и высота его в строках.
Можно  кодировать  спрайт и вручную,  заменяя каждую точку изображения
двумя битами и складывая их в слова, но это менее удобно.
   Теперь приступим к написанию программы:

;Пример вывода спрайта на экран и управления им с клавиатуры
        EMT     14                ;инициализация экрана
;---- вывод изображения (спрайта) на экран ----
BEG:    MOV     #MEN,R0     ;адрес спрайта;
        MOV     ADR,R1      ;адрес ОЗУ, куда будем выводить;
        MOV     (R0)+,R2    ;ширина спрайта в байтах;
        MOV     (R0)+,R3    ;высота спрайта в точечных строках;
A:      MOV     R2,R4       ;начало цикла по строкам;
        MOV     R1,R5       ;запоминаем адрес начала строки;
B:      MOVB    (R0)+,(R1)+ ;цикл вывода одной строки
        SOB     R4,B          ;по байтам;
        MOV     R5,R1       ;восстанавливаем адрес начала строки;
        ADD     #100,R1     ;переходим к следующей строке;
        SOB     R3,A        ;конец цикла по строкам.
;---- временная задержка для замедления движения спрайта ----
        MOV     #4000,R3    ;число 4000 задает длительность задержки
W:      SOB     R3,W
;---- очистка спрайта на экране -----
        MOV     #MEN,R0     ;адрес спрайта;
        MOV     ADR,R1      ;адрес ОЗУ, где будем стирать
        MOV     (R0)+,R2    ;ширина спрайта в байтах;
        MOV     (R0)+,R3    ;высота спрайта в точечных строках;
C:      MOV     R2,R4       ;далее все аналогично выводу спрайта,
        MOV     R1,R5
D:      CLRB    (R1)+         ;только здесь вместо пересылки -
        SOB     R4,D          ;стирание
        MOV     R5,R1
        ADD     #100,R1
        SOB     R3,C
;----------------------------------------------------
        MOV     @#177662,R0 ;код нажатой клавиши в регистр R0
        CMP     R0,#10      ;нажата клавиша "курсор влево"?
        BNE     RIT         ;если нет, идти на метку RIT
        MOV     ADR,R2      ;иначе проверяется, не достигло
        BIC     #177700,R2    ;ли изображение левого края экрана
        BEQ     BEG         ;если достигло, идти на метку BEG
        DEC     ADR         ;иначе уменьшить на 1 адрес изображения
        BR      BEG         ;и идти на метку BEG - переместить изо-
                              ;бражение левее на один байт
;-----------------------------------------------------
RIT:    CMP     R0,#31      ;нажата клавиша "курсор вправо"?
        BNE     UP          ;если нет, идти на метку UP
        MOV     ADR,R2      ;иначе проверяется,
        BIC     #177700,R2  ;достигло ли изображение
        CMP     #76,R2        ;правого края экрана
        BEQ     BEG         ;если достигло, идти на метку BEG
        INC     ADR         ;иначе увеличить адрес изображения на 1
        BR      BEG         ;и идти на метку BEG - переместить изо-
                              ;бражение правее на один байт
;------------------------------------------------------
UP:     CMP     R0,#32      ;нажата клавиша "курсор вверх"?
        BNE     DON         ;если нет, идти на метку DON
        CMP     ADR,#42100  ;изображение достигло верхнего
                              ;края экрана?
        BLO     BEG         ;если достигло, идти на метку BEG
        SUB     #200,ADR    ;иначе адрес изображения уменьшить на 200
        BR      BEG         ;и идти на метку BEG (переместить
                              ;изображение на две строки выше)
;-----------------------------------------------------
DON:    CMP     R0,#33      ;нажата клавиша "курсор вниз"?
        BNE     BEG         ;если нет, идти на метку BEG
        CMP     ADR,#76500  ;иначе проверяется достигло ли
                              ;изображение нижнего края экрана
        BHI     BEG         ;если достигло, идти на метку BEG
        ADD     #200,ADR    ;иначе адрес изображения увеличить на 200
        BR      BEG         ;и идти на метку BEG (переместить
                              ;изображение на две строки ниже)
;-----------------------------------------------------
ADR:   .#56036              ;текущий адрес начала изображения
;---- таблица изображения (спрайта) ----
MEN:   .#2.#12.#1640.#1700.#140300.#37774.#1703.#1700.#1700
       .#6060.#6060.#36074
END

   В приведенной программе в переменной ADR (ячейке памяти, помеченной
меткой ADR) хранится адрес,  по которому рисуется  спрайт  в  экранном
ОЗУ.  С  этого  адреса  начинается вывод левого верхнего угла спрайта.
Начальное значение (56036) определяет,  что в первый  раз  изображение
будет выведено около центра экрана.
   Сначала спрайт выводится в  экранное  ОЗУ  с  заданного  адреса,  а
затем,  после  истечения  некоторой  задержки,  стирается с экрана,  и
программа анализирует код клавиши.  Если нажата любая  клавиша,  кроме
стрелок  управления курсором,  переменная ADR не изменяется,  и спрайт
вновь  рисуется  на  прежнем  месте.  Если  нажата  стрелка,  то   ADR
изменяется,  и  спрайт  будет  в  следующий раз нарисован уже в другом
месте экрана.