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

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

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

     6.1. Использование подпрограмм в кодах при работе на Бейсике

   Одним из  приемов  увеличения  возможностей  программы  на  Бейсике
является   использование   подпрограмм  в  кодах.  Правда,  этот  путь
несколько половинчатый - почему бы сразу не писать  всю  программу  на
ассемблере ?  Поскольку Бейсик по-прежнему занимает много памяти, то и
эффективность этого способа сомнительна.  Использовать подпрограммы  в
кодах   в   Бейсике,  пожалуй,  целесообразно  только  при  отсутствии
описанных в гл.5 инструментальных средств,  а  также  в  тех  случаях,
когда Вы не хотите или не умеете писать программы в кодах,  а улучшить
свою  программу  на  Бейсике  хочется.  В   последнем   случае   можно
использовать чьи-либо готовые подпрограммы в кодах.
   Для подготовки  подпрограммы  в  кодах  с  помощью  Бейсика   часто
используется  такой прием:  коды команд записываются в операторе DATA,
из которого затем в цикле  читаются  оператором  READ  и  заносятся  в
память оператором POKE.
   Для того,  чтобы зарезервировать  достаточно  места  в  памяти  для
размещения  подпрограмм,  необходимо перед началом работы дать команду
CLEAR в непосредственном режиме Бейсика:

                           CLEAR B,A
где
     B - количество байт, отводимых под символьные переменные;
     A - адрес верхней границы памяти, отведенной для программы на
         Бейсике.

   Например, если начальный адрес подпрограммы в кодах равен 30000, то
можно ввести  оператор  CLEAR  250,&O30000 .   После  исполнения этого
оператора транслятор с языка Бейсик  использует  только  область  ОЗУ,
расположенную до адреса 30000. А с адреса 30000 могут быть расположены
подпрограммы в машинных кодах.
   Две команды  Бейсика  позволяют  записывать  и читать с магнитофона
участки ОЗУ,  в которых могут размещаться  подпрограммы  в  кодах  или
таблицы изображений (спрайтов).

                       BSAVE"имя",начало,конец

   По этой команде область  ОЗУ  с  указанным  начальным  и  конечным
адресом записывается на магнитофон в виде файла.
   Программа с МЛ может быть загружена в ОЗУ и запущена на выполнение
командой Бейсика:
                       BLOAD"имя",R,адрес
   Если в данной команде отсутствует первая запятая с буквой  "R",  то
произойдет только загрузка программы в ОЗУ с указанного адреса.
   Обычно, чтобы   не   возиться   с   загрузкой   множества   файлов,
подпрограмму  в  кодах  не  записывают  отдельно,  а  так  и  хранят в
программе на Бейсике в операторе DATA.  При выполнении программы  коды
переписываются в ОЗУ,  потом задается адрес запуска подпрограммы и она
вызывается в любом нужном месте программы на Бейсике.
   Здесь будут  рассмотрены  два примера использования в программах на
языке Бейсик подпрограмм в машинных кодах.

   Пример 1. Вывод спрайта и управление его движением.
   Этот пример  аналогичен  второму  примеру из п.5.3.3.  Для вывода и
стирания спрайта  используются  подпрограммы  в  кодах,  а  управление
движением спрайта осуществляется программой на Бейсике.
   Подпрограммы вывода и стирания  спрайта  аналогичны  приведенным  в
п.5.3.3:

; вывод спрайта на экран
035000     012700          MOV    #MEN,R0
035002     035070
035004     011501          MOV    (R5),R1   ;адрес вывода спрайта
035006     012002          MOV    (R0)+,R2
035010     012003          MOV    (R0)+,R3
035012     010204    A:    MOV    R2,R4
035014     010146          MOV    R1,-(SP)
035016     112021    B:    MOVB   (R0)+,(R1)+
035020     077402          SOB    R4,B
035022     012601          MOV    (SP)+,R1
035024     062701          ADD    #100,R1
035026     000100
035030     077310          SOB    R3,A
035032     000207          RTS    PC        ;выход из подпрограммы
; чистка спрайта на экране
035034     012700          MOV    #MEN,R0
035036     035070
035040     011501          MOV    (R5),R1   ;адрес стирания спрайта
035042     012002          MOV    (R0)+,R2
035044     012003          MOV    (R0)+,R3
035046     010204    C:    MOV    R2,R4
035050     010146          MOV    R1,-(SP)
035052     105021    D:    CLRB   (R1)+
035054     077402          SOB    R4,D
035056     012601          MOV    (SP)+,R1
035060     062701          ADD    #100,R1
035062     000100
035064     077310          SOB    R3,C
035066     000207          RTS    PC        ;выход из подпрограммы
; спрайт, закодированный в формате ГРЕД
035070               MEN: .#2.#12.#1640.#1700.#140300.#37774.#1703
                          .#1700.#1700.#6060.#6060
035116                    .#36074

   В отличие от  примера  п.5.3.3,  где  операции  вывода  и  стирания
спрайта  были  встроены  в  программу,  здесь  они  оформлены  в  виде
подпрограмм,  которые можно вызывать функцией Бейсика  USR.  Выход  из
подпрограммы осуществляется, как обычно, командой RTS PC.
   Одновременно в программе могут быть определены 10 различных функций
USR0..USR9.  В  данном  примере  мы используем только две подпрограммы
(соответственно, функции USR0 и USR1).
   Функция USR позволяет передать подпрограмме в кодах один параметр и
вернуть одно значение того же типа.  Функция USR помещает в  R5  адрес
передаваемого  параметра,  а  в  R3  - его тип (единица в 15-м разряде
означает символьную строку,  информация  о  других  типах  хранится  в
младшем байте R3:  -1 - целый,  0 - вещественный двойной точности, 1 -
вещественный одинарной точности; если аргумент - символьная строка, то
в  качестве аргумента передаются два слова - длина и адрес строки).  В
данном примере мы передаем подпрограммам адрес ОЗУ экрана,  куда нужно
вывести (или где стереть) спрайт.  Поскольку адрес будет размещаться в
переменной целого типа, анализировать R3 нет нужды.
   Отметим также,  что  в  п.5.3.3 "по ходу действия" в R5 сохранялось
значение R1, здесь же мы R1 сохраним в стеке, чтобы не испортить адрес
аргумента.
   Подпрограммы и закодированный спрайт мы разместим с  адреса  35000.
Тогда  перед  набором  программы  (или загрузкой ее с магнитной ленты)
необходимо выполнение оператора CLEAR ,&О35000.  Этот  оператор  можно
вставить   первой   строкой   в   программу,   чтобы   он   выполнялся
автоматически,  но вот беда - не все экземпляры БК-0010-01  отработают
его правильно. Это связано с различиями версий Бейсика в ПЗУ БК разных
лет выпуска.
   Для того,  чтобы  автоматизировать  ввод  подпрограмм,  их  коды мы
разместим в операторе DATA,  откуда  потом  в  цикле  будем  читать  и
переписывать по адресам ОЗУ, начиная с 35000.

10 DATA &O012700,&O035070,&O011501,&O012002,&O012003,&O010204,&O010146
15 DATA &O112021,&O077402,&O012601,&O062701,&O100,&O077310,&O207
20 DATA &O012700,&O035070,&O011501,&O012002,&O012003,&O010204,&O010146
25 DATA &O105021,&O077402,&O012601,&O062701,&O100,&O077310,&O207
30 DATA 2,&O12,&O1640,&O1700,&O140300,&O37774,&O1703
35 DATA &O1700,&O1700,&O6060,&O6060,&O36074
40 ?CHR$(140);CHR$(140)              'инициализируем экран
50 FOR A%=&O35000 TO &O35116 STEP 2% 'записываем подпрограммы в ОЗУ
60 READ D%
70 POKE A%,D%
80 NEXT A%
90 DEF USR0=&O35000     'задаем адрес подпрограммы вывода спрайта
95 DEF USR1=&O35034     'задаем адрес подпрограммы стирания спрайта
100 S%=&O56036          'начальный адрес ОЗУ, где располагается спрайт
110 L%=USR0(S%)         'выводим спрайт
115 FOR L%=0% TO 150%   'задержка
116 NEXT
117 L%=USR1(S%)         'стираем спрайт
120 I%=PEEK(&O177662)   'смотрим, какая клавиша была нажата
130 IF I%=&O10 THEN IF S% MOD 64% <> 0% THEN S%=S%-1%   'влево
140 IF I%=&O31 THEN IF S% MOD 64% < &O76 THEN S%=S%+1%  'вправо
150 IF I%=&O32 THEN IF S%>&O43000 THEN S%=S%-&O200      'вверх
160 IF I%=&O33 THEN IF S%<&O76000 THEN S%=S%+&O200      'вниз
170 GOTO 110

   Строки 50 - 80 производят загрузку команд и данных в область памяти
с адреса 35000 по 35116.
   Оператор DEF (строки  90-95)  определяет  подпрограммы  в  машинных
кодах под именем USR0 и USR1 и задает их начальный адрес.
   Текущее значение адреса спрайта  в  ОЗУ  экрана  хранится  в  целой
переменной S%.
   В строке 120 переменной I%  присваивается код  нажатой  клавиши,  в
зависимости  от  которого в строках 130 - 160 изменяется адрес спрайта
S%.
   Начальная установка  экрана (строка 40) необходима для того,  чтобы
привести экран в исходное состояние,  когда верхний левый угол  экрана
соответствует адресу 40000.

   Пример 2:

10 DATA &O13701,&O35102,&O12737,&O100,&O177716,&O13700,&O35100
12 DATA &O77001,&O12737,0,&O177716,&O13700,&O35100,&O77001,&O77115
14 DATA &O12702,&O400,&O77201,&O207
20 FOR A%=&O35000 TO &O35044 STEP 2
30 READ B%
40 POKE A%,B%
50 NEXT A%
60 DEF USR0=&O35000
70 DATA 128,128,112,128,99,640,128,128,112,128,99,128,128,128
75 DATA 94,128,99,128,128,256,112,128,99,128,112,640
80 FOR C%=1 TO 13
90 READ T%,D%
100 POKE &O35100,T%
110 POKE &O35102,D%
120 I%=USR0(I%)
130 NEXT C%

   Программа на  Бейсике,  приведенная  в  этом  примере,   использует
подпрограмму в машинных кодах для получения мелодии.
   Строки 10-60 подготавливают  в  памяти,  начиная  с  адреса  35000,
подпрограмму в машинных кодах.
   Вся мелодия состоит из звуков.  Количество звуков равно  количеству
пар чисел,  перечисленных в строках 70-75.  В каждой паре чисел первое
число задает тональность, а второе - длительность звука.
   Строки 80-130 "исполняют" мелодию.

          6.2. Использование вещественной арифметики Бейсика
                     при программировании в кодах

   Вы уже знаете, что в системе команд процессора БК нет команд работы
с  вещественными  (действительными)  числами.  Как в Бейсике,  так и в
Фокале работа с вещественными числами организуется чисто программно. В
таком   случае,   почему  бы  не  использовать  записанные  в  ПЗУ  БК
подпрограммы, если надо производить какие-либо вычисления ?
   Использование подпрограмм  вещественной арифметики Фокала описано в
[15].  Здесь мы опишем использование арифметики Бейсика [8].  Сразу же
заметим, что формат вещественного числа в Бейсике и Фокале различен.
   Учтите также,  что применение  описываемого  здесь  метода  сделает
программу  непереносимой  -  она  уже  не сможет работать на БК-0010 с
Фокалом или на БК-0010-01 при  включенном  блоке  МСТД  (равно  как  и
программы,  использующие Фокал, не будут работать с Бейсиком). Хорошим
выходом было бы использование стандартных подпрограмм (из программного
обеспечения Электроники-60) или эмуляторов вещественной арифметики, но
это   доступно,   пожалуй,   только   профессиональным   программстам,
использующим  ЭВМ  более  высокого класса в качестве инструментальной.
Что же касается использования подпрограмм Бейсика,  то платой  за  это
будет  2К  памяти,  так  как область ОЗУ ниже адреса 4000 используется
Бейсиком для своих рабочих переменных и стека.
   Все расчеты  в Бейсике производятся с вещественными числами двойной
точности.  Если в программе на Бейсике  используются  числа  одинарной
точности, то перед вычислениями они все равно преобразуются к двойной.
Каков же формат вещественного числа в Бейсике ?
   Числа двойной точности занимают в памяти 4 слова (8 байт). При этом
старший разряд 1-го слова (63-ий разряд числа) - знак  числа,  разряды
7-14 (разряды 55-62 числа) - порядок (p),  а остальные 6 разрядов 1-го
слова и 2-ое,  3-е,  4-е слова (разряды 0-54  числа)  -  мантисса  (М)
(рис.26).
   Тогда значение числа N определяется по формуле:

         p-201     -1     -2     -3           -54    -55     p-201
N=(M+1) 2     =(1+2  a  +2  a  +2  a  + ... +2   a +2   а ) 2      ,
                      54     53     52            1      0

   где a  - значение i - го бита числа (0 или 1).
        i

   Число 201 -восьмеричное, остальные - десятичные.

│ X (адрес числа)               │ X+2                           │ X+10
│                               │                               │
│63         р а з р я д ы       │           ч и с л а          0│
├─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬─┬─┬─┬─ - - - ───┬─┬─┬─┬─┬─┤
│    порядок (p)  │    м   а   н   т   и   с   с   а  (M)       │
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─┴─┴─ - - - ───┴─┴─┴─┴─┴─┤
│15     разряды слова      2 1 0│                               │
│   1-ое слово (2 байта)        │ 2-ое, 3-е и 4-ое слова(6 байт)│

          Рис.26. Представление числа двойной точности

   Как же перевести число из привычного для  нас  десятичного  вида  в
двоичное представление числа двойной точности ?
   Для этого берется исходное число и делится на 2 до  тех  пор,  пока
его целая часть не будет равна 1.
   Если исходное  число  меньше  1,  то  вместо  деления  производится
умножение на 2 до тех пор,  пока целая часть не станет равна 1.  Затем
дробная часть числа переводится в двоичный код - мантисса готова.  201
(восьмеричное)   плюс   (или   минус,  если  производилось  умножение)
количество делений или умножений на 2 - получим порядок числа.
   Осталось "скомпоновать"   число:   последовательно   слева  направо
записываются знак (0 - для знака "+",  1 - для  знака  "-"),  порядок,
мантисса.  Остальные  биты  мантиссы  заполняются  нулями или мантисса
укорачивается справа так, чтобы общая длина числа равнялась 64 битам.
   Рассмотрим несколько  примеров перевода десятичных чисел в двоичные
числа двойной точности:

   Пример 1:  Исходное число: 9.5 . Делим на 2:

     9.5
     4.75   - результат после 1-го деления:
     2.375  - после 2-го деления;
     1.1875 - после 3-го деления.

   Дробная часть  числа,  равная  0.1875  переводится  в  двоичный код
посредством последовательного умножения на  2.  При  этом  если  после
умножения число стало больше 1, то целая 1 отбрасывается.

     0.1875 - умножается на 2;
     0.375  - умножается на 2;
     0.75   - умножается на 2:
     1.5    - отбрасывается 1 и умножается на 2:
     1      - все.

   Тогда двоичный  код  мантиссы  -  0011 (последовательно сверху вниз
переписывается первый столбец цифр, кроме первого 0). Было произведено
3  деления  на  2,  поэтому  порядок  числа равен 201 + 3 = 204 (числа
восмеричные) или 10000100 (двоичное).  Осталось "скомпоновать"  число:
так как число положительное,  то 63-ий бит равен 0,  тогда число имеет
следующий вид в двоичном коде:

0 10000100 0011 000000000000000000000000000000000000000000000000000.

Если X - адрес числа,  то код этого числа сохраняется по словам памяти
следующим образом:

     Адреса:  Содержимое:
     X         41030 (восьмеричное)
     X+2           0
     X+4           0
     X+6           0

   Пример 2:  Исходное число: -13.25. Отмечаем, что оно отрицательное,
и все остальные операции проделываем с его абсолютным значением. Делим
на 2:
        13.25
         6.625
         3.3125
         1.65625

   Берем дробную часть числа 0.65625 и переводим в двоичный код:

        0.65625
        1.3125
        0.625
        1.25
        0.5
        1

Получаем 10101. Порядок равен 201+3=204 (10000100).
Тогда число в двоичном коде будет равно:

1100001001010100000000000000000000000000000000000000000000000000

или четыре слова: 141124,0,0,0 в восьмеричном виде.

   Пример 3: Исходное число: 0.0234375. Умножаем на 2:

   0.0234375
   0.046875
   0.09375
   0.1875
   0.375
   0.75
   1.5

Переводим дробную часть числа 1.5 равную 0.5 в двоичный код: это будет
1.  Порядок равен 201-6=173 (числа восьмеричные).  Знак - 0 ("+"). Вид
числа в двоичном коде:

0011110111000000000000000000000000000000000000000000000000000000

или четыре слова: 36700,0,0,0 в восьмеричном виде.

   Как же  теперь  использовать  наши  умения  ?  Сначала надо поближе
познакомиться с тем, как Бейсик транслирует свою программу.
   Исходная программа   на  Бейсике  после  ее  запуска  командой  RUN
преобразуется в  последовтельность  адресов  подпрограмм,  реализующих
соответствующие   операторы  Бейсика,  и  исходных  данных:  констант,
адресов переменных и так  далее  [8].  Эти  подпрограммы,  как  и  сам
транслятор, делающий такое преобразование, зашиты (записаны) в ПЗУ.
   После того,  как указанная последовтельность  (ее  называют  "шитым
кодом") получена   в   ОЗУ,  в  регистр  R4  помещается  адрес  начала
последовательности,  и командой "JMP @(R4)+"  начинается  обращение  к
подпрограмме,    адрес    начала    которой    записан   в   указанной
последовательности первым. Передача управления от одной подпрограммы к
другой  осуществляется также по команде "JMP @(R4)+" ,  содержащейся в
конце каждой подпрограммы.
   Если в   программе  на  языке  ассемблера  программисту  необходимо
получить значение какой-либо функции (или арифметической операции), то
он  может  организовать  обращение к готовым подпрограммам,  зашитым в
ПЗУ.  Далее  под  словом  "операция"  подразумевается   арифметическая
операция или функция,  а под словом "операнд" - операнд арифметической
операции или аргумент функции.
   В таблице 19 приводится список адресов подпрограмм, при обращении к
которым   вычисляется   результат   соответствующей    функции    (или
арифметической операции).
   Операнды и результаты операций - числа двойной точности.

                         Таблица 19. Адреса подпрограмм Бейсика
     ┌─────────────────────────┬───────────────┬───────────────┐
     │ Арифметические операции │  Адреса       │ Количество    │
     │   и функции             │ подпрограмм   │ операндов     │
     ├─────────────────────────┼───────────────┼───────────────┤
     │      + (сложение)       │  167144       │     2         │
     │      - (вычитание)      │  167124       │     2         │
     │      / (деление)        │  170776       │     2         │
     │      * (умножение)      │  170210       │     2         │
     │       SQR               │  171350       │     1         │
     │       SIN               │  173614       │     1         │
     │       COS               │  173566       │     1         │
     │       TAN               │  174306       │     1         │
     │       ATN               │  174434       │     1         │
     │       EXP               │  171762       │     1         │
     │       LOG               │  173052       │     1         │
     │       FIX               │  176212       │     1         │
     │       INT               │  176340       │     1         │
     │       RND               │  175176       │     1         │
     │ подпрограмма преобразо- │  166760       │     1         │
     │ вания результата в      │               │               │
     │ целый тип               │               │               │
     └─────────────────────────┴───────────────┴───────────────┘

   Необходимо помнить,  что   для   нормальной   работы   подпрограмм,
указанных  в  таблице  19,  область  памяти с адресами от 2000 до 4000
должна быть свободной (эта  область  -  область  системных  переменных
Бейсика).
   Предлагаем Вашему вниманию небольшую  подпрограмму,  которая  может
облегчить Вам вызов нужных подпрограмм Бейсика.
   Для вызова нужной подпрограммы в регистрах должны быть подготовлены
следующие данные:
   R0 - число операндов (1 или 2);
   R1 - адрес первого операнда;
   R2 - адрес второго операнда (для операций с двумя операндами.
        При операциях с одним операндом значение несущественно);
   R3 - адрес подпрограммы операции (из табл.19);
   R5 - адрес результата.
   Эта вспомогательная подпрограмма работает аналогично транслятору  с
Бейсика  после  команды  RUN  -  формирует требуемый для вызова нужной
подпрограммы шитый код и передает ему управление.
   Последовательность адресов   подпрогрмм   и   данных   (шитый  код)
начинается с адреса A.

FUN: MOV    R0,-(SP)        ; сохранение
     MOV    R1,-(SP)          ; содержимого
     MOV    R2,-(SP)          ; регистров
     MOV    R3,-(SP)          ; в стеке;
     MOV    R4,-(SP)          ;
     MOV    R5,-(SP)          ;
     MOV    #A,R4           ; последовательность адресов
                              ; начинается с адреса А;
     MOV    #156170,(R4)+   ; адрес подпрограммы пересылки в стек;
     MOV    R1,(R4)+        ; адрес первого операнда;
     CMP    R0,#1           ; если операция с одним операндом,
     BEQ    C               ; то идти на метку C;
     MOV    #156170,(R4)+   ; засылка в стек второго операнда;
     MOV    R2,(R4)+        ; адрес второго операнда;
C:   MOV    R3,(R4)+        ; адрес нужной подпрогрммы записы-
                              ; вается в последовательность адресов;
     CMP    R3,#166760      ; если это не подпрограмма перевода
     BNE    G               ; вещественного в целое, идти на G;
     MOV    #156350,(R4)+   ; подпрограмма пересылки слова из стека
     BR     D                 ; (результат целого типа);
G:   MOV    #156334,(R4)+   ; либо пересылка вещественного числа
D:   MOV    R5,(R4)+        ; из стека по адресу, указанному в R5;
     MOV    #B,(R4)+        ; организовать выход из шитого кода;
     MOV    #A,R4           ; запустить шитый код на выполнение;
     JMP    @(R4)+            ;
B:   MOV    (SP)+,R5        ; восстановление
     MOV    (SP)+,R4          ; содержимого
     MOV    (SP)+,R3          ; регистров
     MOV    (SP)+,R2          ; из стека
     MOV    (SP)+,R1          ;
     MOV    (SP)+,R0          ;
     RTS    PC              ; возврат из подпрограммы
A:   .+22

   По команде   "MOV   #156170,(R4)+"   в    эту    последовательность
записывается   адрес  подпрограммы,  производящей  пересылку  значения
переменной в  стек,  а  по  команде  "MOV  R1,(R4)+"  -  адрес   самой
переменной, указанный в R1.
   Аналогично засылается в стек второй операнд, если нужен.
   После выполнения   любой   арифметической   подпрограммы  результат
получается в стеке.  Его необходимо  оттуда  извлечь  и  переслать  по
адресу,  заданному  в R5.  Для этого используется подпрограмма 156334,
пересылающая вещественное число (4 слова),  либо 156350,  пересылающая
одно слово, если результат целый.
   Пример цепочечных вычислений:

; Вычисляем выражение (4+7)*3.0625-1.25
     MOV    #2,R0           ;двухоперандная команда
     MOV    #AR1,R1         ;первое слагаемое
     MOV    #AR2,R2         ;второе слагаемое
     MOV    #167144,R3      ;адрес подпрограммы сложения
     MOV    #REZ,R5         ;адрес результата
     JSR    PC,FUN          ; складываем
     MOV    R5,R1           ;результат используем
                              ;как первый сомножитель
     MOV    #AR3,R2         ;второй сомножитель
     MOV    #170210,R3      ;адрес подпрограммы умножения
     JSR    PC,FUN          ; умножаем (результат там же - в REZ)
     MOV    R5,R1           ;результат используем как уменьшаемое
     MOV    #AR4,R2           ;вычитаемое
     MOV    #167124,R3      ;адрес подпрограммы вычитания
     JSR    PC,FUN          ; вычитаем
; Печатаем полученный результат на дисплей
     ADD    #10,R5          ;засылаем результат в стек;
     MOV    -(R5),-(SP)
     MOV    -(R5),-(SP)
     MOV    -(R5),-(SP)
     MOV    -(R5),-(SP)
     JSR    PC,@#164710     ;вызываем подпрограмму перевода
                              ;числа в символьную строку;
     ADD    #4,SP           ;восстанавливаем стек;
     MOV    #3027,R1        ;адрес символьной строки
     MOV    #20000,R2       ;ограничитель строки - пробел
     EMT    20              ;печатаем сформированную строку
; Аргументы:
AR1: .#40600.#0.#0.#0       ;4.0
AR2: .#40740.#0.#0.#0       ;7.0
AR3: .#40504.#0.#0.#0       ;3.0625
AR4: .#40240.#0.#0.#0       ;1.25
REZ: .#0.#0.#0.#0           ;результат

         6.3. Использование системных переменных МОНИТОРа БК

   Область ОЗУ  в  диапазоне адресов от 0 до 377 МОНИТОР БК использует
для хранения своих рабочих переменных (используются те адреса, которые
не   заняты  векторами  прерывания,  см.п.4.4.3).  В  этих  переменных
указываются текущие режимы работы устройств,  например,  режимы работы
дисплея.  Зная  о  назначении тех или иных переменных,  можно добиться
таких  эффектов,  которые  невозможны  при   "штатном"   использовании
системного программного обеспечения.
   Рассмотрим несколько   примеров   использования   системных  ячеек.
Примеры даны на Бейсике и на языке ассемблера.

                                 Таблица 20. Системные переменные
   ┌─────┬─────┬─────────────────────────────────────────────────┐
   │Адрес│Длина│           Назначение                            │
   │     │байт │                                                 │
   ├─────┼─────┼─────────────────────────────────────────────────┤
   │ 040 │  1  │ Режим 32/64 (0 - режим "64", 377 - режим "32")  │
   │ 041 │  1  │ Инверсия экрана (0 - выкл., 377 - вкл.)         │
   │ 042 │  1  │ Режим расширенной памяти (0 - выкл., 377 - вкл.)│
   │ 043 │  1  │ Регистр (0 - LAT, 200 - РУС )                   │
   │ 044 │  1  │ Подчеркивание (0 - выкл., 377 - вкл.)           │
   │ 045 │  1  │ Инверсия символа (0 - выкл., 377 - вкл.)        │
   │ 046 │  1  │ Режим ИСУ (0 - выкл., 377 - вкл.)               │
   │ 047 │  1  │ Режим БЛР (0 - выкл., 377 - вкл.)               │
   │ 050 │  1  │ Режим ГРАФ (0 - выкл., 377 - вкл.)              │
   │ 051 │  1  │ Режим ЗАП (0 - выкл., 377 - вкл.)               │
   │ 052 │  1  │ Режим СТИР (0 - выкл., 377 - вкл.)              │
   │ 053 │  1  │ Режим 32/64 в служебной строке (0-"64",377-"32")│
   │ 054 │  1  │ Подчеркивание в служ. строке (0-выкл., 377-вкл.)│
   │ 055 │  1  │ Инверсия служ. строки (0 - выкл., 377 - вкл.)   │
   │ 056 │  1  │ Гашение курсора (0 - выкл., 377 - вкл.)         │
   │ 104 │  1  │ Код последнего введенного символа               │
   │ 112 │ 10  │ Положения табуляторов (побитно при режиме "64") │
   │ 126 │  2  │ Адрес буфера программируемого ключа К10         │
   │ 130 │  2  │ Адрес буфера программируемого ключа К1          │
   │ 132 │  2  │ Адрес буфера программируемого ключа К2          │
   │ 134 │  2  │ Адрес буфера программируемого ключа К3          │
   │ 136 │  2  │ Адрес буфера программируемого ключа К4          │
   │ 140 │  2  │ Адрес буфера программируемого ключа К5          │
   │ 142 │  2  │ Адрес буфера программируемого ключа К6          │
   │ 144 │  2  │ Адрес буфера программируемого ключа К7          │
   │ 146 │  2  │ Адрес буфера программируемого ключа К8          │
   │ 150 │  2  │ Адрес буфера программируемого ключа К9          │
   │ 153 │  1  │ Тип операции в режиме ГРАФ (0 - СТИР, 1 - ЗАП)  │
   │ 154 │  2  │ Унитарный код положения графического курсора    │
   │ 160 │  2  │ Абсолютный адрес курсора в ОЗУ экрана           │
   │ 162 │  2  │ Константа смещения одного алфавитно-цифрового   │
   │     │     │ символа относительно другого                    │
   │ 164 │  2  │ Длина ОЗУ экрана в символах                     │
   │ 176 │  2  │ Координата Х последней графической точки        │
   │ 200 │  2  │ Координата Y последней графической точки        │
   │ 202 │  2  │ Начальный адрес ОЗУ экрана                      │
   │ 204 │  2  │ Адрес начала информационной части экрана        │
   │     │     │ минус 40000                                     │
   │ 206 │  2  │ Длина ОЗУ экрана в байтах                       │
   │ 210 │  2  │ Длина информационной части экрана               │
   │ 212 │  2  │ Код цвета фона на экране                        │
   │ 214 │  2  │ Код цвета символа на экране                     │
   │ 216 │  2  │ Код цвета фона в служебной строке               │
   │ 220 │  2  │ Код цвета символа в служебной строке            │
   │ 260 │  2  │ Адрес подпрограммы, выполняемой при прерывании  │
   │     │     │ по вектору 60. Если 0, то ничего не выполняется │
   │ 262 │  2  │ Если 0, то код клавиши "ВК" равен 12, иначе 15  │
   │ 264 │  2  │ Начальный адрес загруженной программы           │
   │ 266 │  2  │ Длина загруженной программы                     │
   └─────┴─────┴─────────────────────────────────────────────────┘
   Примечание: В  буфере   программируемого   ключа   записана   длина
соответствующего ключа (в символах), далее - сами символы.

   Пример 1.   Печать красными буквами на синем фоне:

10 POKE &O214,-1        MOV    #177777,@#214  ;красный цвет;
20 POKE &O212,&O52525   MOV    #52525,@#212   ;синий фон;
30 ?"Приветик!"         MOV    #TXT,R1        ;печатаем
                        CLR    R2               ;текст
                        EMT    20               ;
                        HALT
                   TXT: .A:Приветик!

   Пример 2.   Запись символов в служебную строку:

10 POKE &O160,PEEK(&O204)+&O36100       MOV  @#204,@#160
                                        ADD  #36100,@#160
20 ? "текст"                            MOV #TXT,R1
                                        CLR R2
                                        EMT 20
                                        HALT
                                   TXT: .A:текст

   Примечание: эта последовательность  проходит  только  в  программе;
если  попытаться  проделать  ее  в  непосредственном режиме,  то после
оператора POKE ...  Бейсик напишет "Ок" в служебной  строке  и  курсор
вернется на свое прежнее место.

   Пример 3.   Печать "в столбик":

10 X=PEEK(&O162)                MOV @#162,R3   ;запомнить старый режим
20 POKE &O162,&O100             MOV #100,@#162
30 ?"текст"                     MOV #TXT,R1
                                CLR R2
                                EMT 20
40 POKE &O162,X                 MOV R3,@#162   ;восстановить режим
                                HALT
                           TXT: .A:текст