# Кросс Ассемблер БК Турбо8.

Сделан на основе алгоритмов Turbo8DK для Andos. Хотя, теперь уже от оригинала мало что осталось, название да пара идей.

Комплект: |

1. BKTurbo8.exe - кросс компилятор, линковщик.
2. BKbin2obj.exe - конвертер массивов данных в объектные модули чтобы прилинковывать их к скомпилированным ассемблерным программам. Можно считать, что эта утилита устарела после добавления в кроссассемблер псевдокоманды .insert, но эта утилита может ещё и картинки на примитивном уровне обрабатывать.
3. BKTools.exe - потенциально многоцелевая утилита, но сейчас я её использую только как распаковщик/упаковщик BKPack. Причём как недавно выяснилось, она не понимает старый формат BKPack, о существовании которого ни я, ни, наверное, многие и не подозревали.
4. ARIFM.ASM - модуль выполнения команд расширенной арифметики, при необходимости, скомпилировать в объектный файл и слинковать со своей программой.
5. BKTurbo8.html - этот файл документации. Этот файл создан на основе оригинальной документации к Turbo8DK, добавлено от себя и взято немного таблиц из других мест, поэтому разница в стилистике и формулировках прямо бросаются в глаза. И текст местами сумбурный и путаный.
6. Примеры применения и использования.

Максимальная длина исходного текста не ограничена.

Максимальная длина получаемой программы - 0177000 байтов.

Формат объектного файла не совместим с форматом оригинального Turbo8DK из-за того, что в кросс ассемблере применяются метки неограниченной длины и полноценные арифметические выражения, поэтому пришлось выдумывать свой.

## ОБЩИЕ СВЕДЕНИЯ

Оригинальная система программирования на языке Ассемблера Turbo8DK являлась дальнейшим развитием ассемблеров серии TURBO (MicroWS, TURBO4H, TURBO5M, TURBO6M) и предназначалась для работы на БК10 с дополнительным ОЗУ 16К и на БК11.

Кросс ассемблер БКТурбо8 предназначен для компиляции ассемблерных программ и создания исполняемых бинарных модулей, которые потом можно запускать на БК-0010(-01) и БК-0011(М).

Кросс ассемблер системонезависимый и вообще ни от чего не зависимый. При успешной компиляции создаётся бинарник, который будет работать так и там, как и где задумано разработчиком компилируемой программы. Т.е. хоть под набор EMTов БК-0010, хоть - БК-0011, а хоть и RT-11, правда без макросов это сильно неудобно и кросс ассемблер предназначен совсем не для RT-11.

Генерируется .bin файл, у которого первым словом идёт адрес загрузки, вторым - размер файла, совместимый с эмуляторами. Этот файл можно закинуть в образ с помощью BKDE или прямо запустить его в эмуляторе. Так же возможна генерация .raw файла без заголовка .bin для нужд пользователя. Ещё можно генерировать .sav файлы, чтобы запускать их в системе ОС-БК, это не делает кроссассемблер полноценным MACRO-11, просто даёт возможность без дополнительных усилий создавать простые .sav файлы.

### Некоторые особенности ассемблеров серии Turbo.

Это касается оригинальной версии Turbo8DK.

1. В арифметике над метками введено деление на 2, что удобно для работы с массивами слов.
2. Отсутствуют не сильно нужные псевдокоманды .TTYIN, .TTYOUT, .ENABL, .DSABL и присвоение имён регистрам.
3. Добавлена псевдокоманда .ADDR.
4. При трансляции проверяется чётность адреса команды и величина аргумента .BLKB и .BLKW.
5. Программа транслируется с адреса 01000 по умолчанию, либо с адреса, указанного псевдокомандой .LA.
6. Во всех случаях правильно работает арифметика над метками.

### Отличия от оригинальной программы и дополнения.

1. Реализована регистронезависимость для меток, команд ассемблера, псевдокоманд, имён регистров и вообще всего, что не является строковым аргументом псевдокоманд .ASCII, .ASCIZ, .RAD50, операторов ' и ".
2. В присваиваниях, арифметических выражениях, перечислениях между аргументами допускаются пробелы и табуляции для улучшения наглядности и читабельности текста. В синтаксисе элементов адресаций пробелы не допускаются.
3. Длина метки ничем не ограничена, буквально. Т.е. ограничена объёмом типа std::string. Надо бы ввести какие-то разумные ограничения, но пока нет смысла, будем надеяться на благоразумность пользователей.
4. Присваивания типа name = <выражение> можно использовать в любом месте текста, а не только в начале.
5. Адрес трансляции программы можно задать ключом командной строки "-s". Данный ключ имеет меньший приоритет перед псевдокомандой .LA.
6. Псевдокоманде .LA добавлен синоним .LINK. Можно писать и так, и сяк, но только один раз.
7. Можно писать так: .byte <выражение>, но результат должен вмещаться в байт, иначе ошибка.
8. Можно писать так: .blkb <выражение> или .blkw <выражение>, но при этом метки и константы выражения должны быть уже определены, чтобы выражение можно было вычислить, иначе ошибка. Т.к. эти псевдокоманды влияют на изменение текущего адреса компиляции.
9. Добавлена псевдокоманда .org <выражение>. Она выравнивает текущий указатель адреса по результату выражения. Допускается нечётное значение. Выражение так же должно быть вычислимым, т.е. все метки и константы должны быть определены. Если результат выражения меньше текущего указателя - ничего не происходит. Но эту команду нельзя использовать в линкуемых объектных модулях. Т.к. она влияет на изменение текущего адреса компиляции, и результат получается некорректный.
10. Точку (указатель на текущий адрес) теперь можно использовать в модулях, которые компилируются в режиме CL, и линкуются потом к другим модулям. Но опять же . = <выражение> использовать линкуемых объектных модулях нельзя, в этом случае, в линкуемом модуле, как и с командой .org получится некорректный результат.
11. Введены C-подобные комментарии '//' (полностью аналогично обычному комментарию ';') и многострочный комментарий /**/. (Просто так. В качестве эксперимента, что и как ещё можно расширить и улучшить при используемых алгоритмах.) Многострочный комментарий можно использовать как инлайн-комментарий, после него можно писать код. Но его нельзя писать в середине команды.
12. Добавлены расширенные форматы записи чисел, имеющие синтаксис Си и MACRO-11.
13. Добавлены псевдокоманды .flt2 и .flt4 для работы с плавающими числами, а так же поддержка ассемблерных инструкций EIS, FIS, FPU.
14. Добавлено включение дополнительных исходных текстов в компилируемую программу псевдокомандой .include "имя файла", вложенность не ограничена, сделана защита от рекурсивной вложенности, включённые ранее файлы повторно не включаются.
15. Добавлены скрипты для модификации бинарного файла сразу после компиляции.
16. Реализованы полноценные арифметические выражения со скобками. В выражениях в качестве аргумента можно использовать локальные метки (с некоторыми ограничениями). Скобки допускаются треугольные и квадратные (круглые - конфликтуют с режимами адресации, поэтому не используются).
17. Добавлены назад команды .ENABL, .DSABL.
18. Добавлено назад присваивание имён-синонимов регистрам, а так же к регистрам можно обращаться по их базовым именам: %0 .. %7.
19. Добавлена псевдокоманда .insert "имя файла". Действие - вставка содержимого файла как байтового массива в генерируемый бинарный файл непосредственно на этапе компиляции.
20. Добавлена псевдокоманда .align <выражение>, выравнивание по границе, являющейся степенью двойки.

### Недостатки и ограничения.

1. Объектный модуль кросс ассемблера имеет собственный формат и не совместим вообще ни с чем, и что самое досадное - с оригинальным форматом Turbo8DK. Хотя, это уже, наверное, не столь досадно.
2. Отсутствует пакетная обработка из файлов, содержащих списки обрабатываемых файлов. Приходится писать портянки из однородной копипасты в bat файлах.

### Режим работы.

Кросс ассемблер представляет собой консольную программу, принимающую все необходимые параметры с командной строки. Краткий список параметров можно узнать у самого кросс ассемблера командой -? (--help).

Имеется два режима работы.

- Режим компиляции, с созданием объектных модулей и/или исполняемого файла.
- Режим линковки объектных модулей.

#### 1. Режим компиляции.

BKTurbo8 [-i<c>][-d][-r|-x][-l[name]][-o[name]][-t[name]][-s<0addr>] <cmd> <file_1 *[ file_n]>

-i<c> (--input <c>) - задать кодировку исходного файла.
| Возможные кодировки:

- a - автоопределение (по умолчанию)
- k - KOI8-R
- o - OEM CP866
- w - ANSI CP1251
- 8 - UTF8
- u - UNICODE UTF16LE

Если автоопределение определило кодировку некорректно, необходимо вручную задать верную кодировку данным ключом.

Если в тексте очень мало русских букв, то кодировка очень часто определяется неверно. UTF8 без BOM тоже, как правило, определяется неверно.

-d (--details) - вывод большего количества информации на экран.
| На данный момент дополнительно выводится таблица меток программы.
-r (--raw) - создавать просто бинарный массив, не использовать формат .bin.
-x (--sav) - создавать файл в формате .sav. Этот ключ отменяет действие ключа -r, если он был задан и имеет более высокий приоритет. Создаётся простой sav файл без заголовка .bin.
| Ключи -r и -x не имеет смысла использовать вместе, действие ключа -x перекрывает действие ключа -r.
-l[name] (--listing[=name]) - генерировать файл листинга программы с расширением lst.
| Если имя файла задано, то используется оно для генерации листинга, если нет - то берётся имя файла исходного текста.
-o[name] (--object[=name]) - генерировать объектный файл.
| Если имя файла задано, то используется оно для генерации объектного файла, если нет - то берётся имя файла исходного текста.
-t[name] (--table[=name]) - создавать особый объектный файл, в котором содержатся только глобальные метки.
| Создаётся файл с именем name_tbl.obj – чтобы ни с чем не перепутать. Это необходимо в экзотических случаях, когда собираются отдельные оверлейные модули, работающие в заданных окнах БК11, но при этом использующие подпрограммы из основного тела программы, расположенной в странице 0, или каком-то другом постоянном месте.

О назначении и способе использования см. в разделе КОМПИЛЯЦИЯ.

-s<0addr> (--address <0addr>) - задать начальный адрес компиляции.
| Адрес задаётся в восьмеричном виде, цифра 0 впереди обязательна. Этот адрес не имеет приоритета, и если в тексте программы используется псевдокоманда .LA, то применяется адрес псевдокоманды.
<cmd> - команда компиляции:
| CO - полная компиляция. В результате, при отсутствии ошибок создаётся бинарный исполняемый файл и опционально заданные соответствующими ключами дополнительные файлы. Эта команда выполняет действия аналогичные командам CL + LI. CL - компиляция в объектный файл для дальнейшей линковки с другими объектными файлами. В результате, при отсутствии ошибок, всегда создаётся объектный файл. Бинарный файл не создаётся.

Файл листинга создаётся в любом случае, при наличии ошибок, код ошибки и его текстовое пояснение помещаются перед строкой листинга, вызвавшей ошибку.

В конец файла листинга записывается таблица глобальных меток, а также список ссылок на неопределённые метки, если они есть.

N.B. В режиме CL все ссылки на метки считаются неопределёнными, т.к. компоновка производится только для команд ветвления и SOB поэтому не нужно удивляться огромным спискам неопределённых меток.

N.B.2 Разница в генерируемых объектных файлах в этих режимах принципиальна. В режиме CO объектный файл уже скомпонован по заданному адресу компиляции и не является перемещаемым.

Его можно подлинковать к другому файлу только в качестве массива данных, чтобы потом например переместить его по заданному адресу или как-либо обработать подобным образом.

<file_1 *[ file_n]> - список исходных файлов, перечисленных через пробел.
| Допускаются маски файлов.

#### 2. Режим линковки.

BKTurbo8 [-d][-r|-x][-l[name]][-o[name]][-t[name]][-s<0addr>] LI <outfile> <file_1 *[ file_n]>

| Ключ -i не используется. Ключи -d, -r, -x, -l, -o, -t и -s имеют тот же смысл, что и в режиме компиляции.
Команда линковки - LI
| Команда линковки - LI, за командой следует обязательное имя выходного файла <outfile>, маска файла не допускается. А затем список файлов объектных модулей, перечисленных через пробелы. Листинг программы при этом не создаётся, потому что не из чего, но если задан ключ -l, в файл листинга сохраняется список меток, а так же список ссылок на неопределённые метки, если они есть.

Вот для этого он, в принципе, и нужен - посмотреть, есть ли неопределённые метки и исправить эту ситуацию.

<outfile> - имя создаваемого исполняемого файла.
| Обязательный параметр, если его забыть, то за имя исполняемго файла будет взято имя первого объектного файла, и всё будет испорчего, а если там ещё и маска файла будет, то будет вообще ошибка.
<file_1 *[ file_n]> - список исходных файлов, перечисленных через пробел.
| Допускаются маски файлов.

Порядок следования ключей командной строки не имеет значения, но команда всегда должна располагаться после всех ключей. После команды LI первый аргумент - имя создаваемого файла. Если про это забыть, то будет создан выходной файл с именем file_1, а файл file_1.obj не будет слинкован и вообще будет перезаписан, что вообще плохо. Мало того, что вы получите неверный результат, вам придётся ещё и заново создавать потерянный объектный файл.

Если используются длинные имена ключей, то тут есть нюанс: есть ключи с опциональными параметрами и есть с обязательными. Оказалось, что парсер командной строки не может опознать опциональный параметр длинного ключа, если он записан через пробел за ключом. С обязательными параметрами такой проблемы нет. Поэтому задавать параметр надо через знак '=' (равно). Вот так: "--listing=filename". Ну, или не париться и использовать короткие имена ключей.

### Коды ошибок программы.

После завершения работы программа возвращает следующие коды ошибок:

- 0 - нет ошибок, все операции были завершены успешно.
- число больше нуля - количество найденных синтаксических ошибок при компиляции. В результате не создаётся объектный или бинарный файл.
- -1 - ошибка в командной строке. Это когда недостаточно аргументов или неверные параметры, БКТурбо вам скажет, в чём дело.
- -2 - недостаточно памяти. Это маловероятно, но всё же.
- -3 - не определена кодировка исходного файла. Эта ошибка возникает при неудачном автоопределении кодировки. В этом случае кодировку надо задавать принудительно командой командной строки.
- -4 - не удалось открыть исходный файл.
- -5 - не удалось открыть объектный файл.

## АССЕМБЛЕР

Исходная программа записывается в виде последовательности команд, причём в одной строке может быть несколько команд или псевдокоманд, разделённых пробелами. Между меткой и командой, между командой и операндами, а также между операндами и комментарием может быть произвольное количество пробелов (не менее одного). Операнды разделяются запятыми, между которыми могут быть пробелы для улучшения читабельности. Метка не обязательно должна быть в одной строке с командой или псевдокомандой, она может быть в предыдущей строке.

    МЕТКА: КОМАНДА(ПСЕВДОКОМАНДА) ОПЕРАНДЫ ;КОММЕНТАРИЙ

    МЕТКА1: МЕТКА2: //КОММЕНТАРИЙ

Примеры использования многострочного комментария:

     /*
    Пример многострочного
    комментария
    */

    Mov R0,R1 /*пересылка*/ inc R0

Да, вот так, комментарий /**/ можно использовать как инлайн комментарий, и за ним может располагаться команда. Однако этот комментарий нельзя использовать внутри команды и её операндов.

МЕТКА: - Имя, определяемое пользователем, которому соответствует адрес трансляции. Длина метки ничем не ограничена, метка ограничивается двоеточием (:), пробелы между меткой и двоеточием недопустимы. Метка может содержать латинские буквы 'A'..'Z', цифры '0'..'9', а также знаки '_', '.', '$'.

Глобальная метка - любое допустимое слово, начинающееся с символов 'A'..'Z', '_' или '$'.

Локальная метка - любое допустимое слово, начинающееся с цифры '0'..'9'. В отличие от MACRO-11, где локальные метки состоят только из цифр и завершаются знаком '$', здесь в локальной метке могут быть и буквы, и остальные допустимые знаки. Ещё одно хорошее отличие - возможен переход по локальной метке вперёд через глобальную, но это справедливо только для команд ветвления. Однако, в арифметических выражениях можно использовать только локальные метки, оканчивающиеся знаком '$', по которому и определяется, что это локальная метка.

Метка не может начинаться с символа '.' (точка), но потом этот символ можно использовать в неограниченном количестве в обоих типах меток.

В командах из двух или трёх слов можно использовать как глобальные метки, так и локальные. Однако диапазон видимости локальных меток ограничен глобальными метками. Просмотр вперёд, через глобальную метку невозможен.

В поле меток может быть несколько меток, которые будут иметь одно и то же значение.

Если при трансляции встречается ранее определённая метка или присваивание, то выдаётся сообщение об ошибке.

Не может быть совпадающих имён метки и присваивания. Это считается ошибкой.

Метку можно поставить перед любой командой или псевдокомандой. Если её поставить перед псевдокомандой .END, то её адрес будет следующим после последнего адреса программы.

Пример:

    START:STOP:  BIC  #177770, R0
                 CALL RE
                 . . . . .
    RE:          CMP  R0 , #7

Здесь метки START и STOP имеют одно и то же значение, и обращение может производиться по любой из них.

## ПРЯМОЕ ПРИСВАИВАНИЕ.

Присваивает имени определённое значение. Имя принимает абсолютное значение (но не для скриптов). Присваивание допустимо в любом месте текста программы, значения меток, которые являются аргументами присваивания, могут быть не определёнными на момент присваивания. В таком случае присваивание не считается действительным до момента полного вычисления арифметического выражения.

Если и после финальной компоновки выражение не сможет быть вычислено, то присваивание будет считаться неопределённой меткой.

Пример:

    START=1000
    STOP = START + 2000

В качестве аргумента присваивания можно использовать символ '.'.

Так же справа от знака '=' может стоять полноценное арифметическое выражение, аргументом которого может быть символ '.'.

Слева тоже может стоять символ '.'. Это довольно опасное действие, мгновенно меняющее текущее значение pc.

Пример:

    . = 3000
    nop nop nop
    . = . + 1000
    nop nop nop
    . = . - 4014
    nop nop nop

Злоупотребляя этим, можно писать код, который сам себя затирает последующим кодом. И вообще, получать разные странные результаты.

Есть специальный вид присваивания, полезный только для скриптов.

    FLAG == 1

Двойной знак равенства, он задаёт абсолютное значение определения, которое при использовании никак не корректируется. Это необходимо в основном для скриптов.

Проблема скриптов наконец-то решена и такой вид присваивания больше не нужен. Но функционал остался, хоть сейчас и не используется, но вдруг пригодится когда-нибудь.

## ИМЕНА РЕГИСТРОВ.

Обращение к регистру можно делать по его базовому имени:

%0 %1 %2 %3 %4 %5 %6 %7

Либо по общепринятым именам:

R0 R1 R2 R3 R4 R5 SP PC

К регистрам SP и PC можно обращаться так же и по именам R6 и R7 соответственно. Эта возможность оставлена на всякий случай, для частичной совместимости с другими диалектами ассемблеров БК, например, Micro.

А также есть возможность присвоить имени регистра своё обозначение, называемое синонимом имени регистра.

Синонимов можно назначить неограниченное количество, повторяющиеся синонимы - игнорируются. И одновременно обращаться к регистру по любому из них.

Назначение можно делать в любом месте программы, но обращаться по новому имени можно будет, естественно, только после назначения.

При желании можно назначить каждому регистру много синонимов и сделать программу максимально запутанной.

А можно назначить нескольким регистрам одинаковые синонимы и сделать программу неработоспособной.

Присвоение делается так:

%n = имя

Т.е. базовому имени, и только ему, присваивается новое имя. Имя может начинаться только с буквы и знаков '_' и '$', которые тоже считаются буквами, затем может содержать любые допустимые символы: буквы, цифры и точку.

Однако есть ограничение - синоним не должен совпадать с именем метки или присваивания. Проверок на этот счёт не делается, но у имени регистра приоритет, поэтому такое имя, если оно одно единственное в аргументе (например относительная адресация) будет считаться регистром и будет у вас скомпилировано совсем не то, что ожидалось. А в арифметических операциях, особенно в скобках - это имя уже рассматривается как имя метки.

Пример.

    ; Пример назначения синонимов регистрам на примере
    ; реальной проги "Загрузчик сдаточных тестов"

        %0 = AX
        %1 = BX

            mov     #100, @#177660
            mov     #100000, AX     ;сперва переместим перемещатель
            mov     #mover_end, BX  ;в верхние адреса
    1$:     mov     -(BX), -(AX)
            cmp     BX, #mover_begin
            bhi     1$
            jmp     (AX)            ;запустим перемещатель

        %1 = SRC
        %0 = DST

    mover_begin:
            mov     #TST_BEGIN, SRC ;начало массива тестов
            mov     #TST_END, %2    ;конец массива тестов
            clr     DST             ;адрес, куда переместить массив
    1$:     mov     (SRC)+, (DST)+
            cmp     SRC, %2
            blo     1$
            mov     @#24, DST       ;берём точку входа
            jmp     (DST)           ;запускаем тест
            mov     #2$, DST        ;это тест использования локальных меток
            mov     2$(DST), DST    ;в арифметическом выражении, случайно сюда попал
            .blkb   10
    2$:
    mover_end:
    TST_BEGIN:
            .org 1400
    TST_END:
            .end

## АРИФМЕТИЧЕСКИЕ ВЫРАЖЕНИЯ.

Практически везде, где возможно, можно писать полноценные арифметические выражения: в ассемблерных командах, в псевдокомандах, в присваиваниях.

Операндами арифметического выражения могут быть числа, метки глобальные и локальные и определения (присваивания).

Операторы в порядке повышения приоритета:

- Логическое ИЛИ ( | ), Логическое ИСКЛЮЧАЮЩЕЕ ИЛИ ( ^ ).
- Логическое И ( & ).
- Сложение ( + ), Вычитание ( - ).
- Умножение ( * ), Деление ( / ), Остаток от деления ( % ), Арифметический сдвиг влево ( << ), Арифметический сдвиг вправо ( >> ).
- Унарный плюс ( + ), Унарный минус ( - ), Унарное инвертирование ( ~ ).

Для изменения приоритета можно использовать скобки. Треугольные ( <, > ), как в MACRO-11, или прямоугольные ( [, ] ). Но если скобок много, и они вложенные - треугольные скобки начинают конфликтовать с арифметическими сдвигами. Поэтому, когда много вложенных скобок, их использовать не рекомендуется. Либо можно чередовать их с квадратными, но нельзя смешивать. Если открывающая скобка была треугольной, то и закрывающая должна быть треугольной.

Примеры:

    .word [[[VAL1+3]*[4+VAL2]] +5] << [VAL3-2] ; когда много скобок, треугольные закрывающие скобки
                                          ; интерпретируются как shr, поэтому используем квадратные
    .word <VAL4*5-6/VAL5> ^ [VAL6|1]

В общем, можно использовать какие хочешь вычисления и ни в чём себе не отказывать.

### Форматы чисел.

Числом считается последовательность цифр от 0 до 7 по умолчанию в восьмеричной форме счисления.

Если на конце числа стоит точка, то это признак десятичного числа, ну и там уже можно использовать цифры 8 и 9.

Например:

MOV #10., R1 аналогично MOV #12, R1

Кроме обычной записи восьмеричных и десятичных чисел с точкой на конце добавлены следующие возможные записи чисел:

- С-подобная префиксная форма записи
  - 0x abcd - 16-ричное число
  - 0d 9999 - десятичное число (не знаю зачем, но такое возможно)
  - 0b 1111 - двоичное число

  При этом форма записи 0123. - хоть и начинается с нуля и должно по сишной логике быть восьмеричным числом, но из-за точки на конце распознаётся как десятичное, потому что по-другому не получается, чтобы не ломать всю логику парсинга и совместимость. Поэтому же из-за точки не распознаются числа с плавающей точкой.

- префиксная форма из MACRO-11
  - ^x abcd, ^h abcd - 16-ричное число
  - ^d 9999 - десятичное число
  - ^b 1111 - двоичное число
  - ^o 7777 - восьмеричное число (просто до кучи, чтобы всё было)
  - ^f 12.3 - число с плавающей точкой однословное.
  - ^r ABC - три символа в кодировке RADIX-50

Для совместимости оставлен унарный оператор инвертирования ^c из MACRO-11, который инвертирует следующий за ним операнд (это может быть, как число в любой из перечисленных выше форм записи, так и метка и абсолютный адрес, что, в общем-то, бессмысленно, но синтаксически допустимо). Он полностью аналогичен унарному оператору ~. Между оператором и операндом допускаются пробелы для улучшения читабельности.

Примеры:

    mov     #0xdead, R0
    mov     #0b1111000011110000, R0
    mov     #^xfaad, R0

    .word   ^hFFFF, ^b1110001111000010
    .word   ^o777, ^rONE

    mov     #^f256.0, R1

    mov     #^c101, R1
    mov     #^c 0x85, R1

    .word   ^c 25. + ^c "YE

Естественно, можно записывать отрицательные числа, они рассматриваются как арифметические выражения с унарным минусом. Так же можно писать перед числом унарный плюс, хоть этого никто и не делает. К тому же унарных знаков может быть неограниченное количество: "--~--~+-+++-^c-+-+-++-^c-3" - эта чудовищная конструкция синтаксически верна.

Примеры:

    mov     #-0xdead, R0
    mov     #-0b1111000011110000, R0
    mov     #-^xfaad, R0

    .word   -^hFFFF, ~^b1110001111000010
    .word   -^o777, -^rONE

    mov     #-^f256.0, R1

    mov     #-^c101, R1
    mov     #-^c 0x85, R1

    .word   ^c-25. + -^c "YE

Так же числом считается запись одного кода ASCII, как число ( 'A ) и запись двух кодов ASCII, как число ( "AB ) и знак Точка - значение адреса первого слова команды (а не текущего слова, куда должен быть сохранён результат).

Например:

MOV #'A,R1 то же, что MOV #101,R1

Можно использовать в любом случае вместо числа, например:

TRAP '& вместо TRAP 46.

Вообще, аргумент команд TRAP и EMT может быть полноценным арифметическим выражением, в котором запрещены локальные метки, но разрешены неопределённые, и результат должен укладываться в байт, иначе будет ошибка переполнения и некорректный результат.

То же касается и команды MARK, но в арифметическом выражении её аргумента запрещены как локальные, так и неопределённые метки, т.к. выражение должно быть вычислено немедленно, чтобы сформировать опкод команды.

MOV #"AB,R1 (два байта)

Точку удобно использовать для задания смещения для перехода.

ПРИМЕЧАНИЕ: точка может быть использована в качестве любого аргумента команды, даже можно использовать точку с именами меток при трансляции объектного модуля (CL).

Кроме присваивания точке нового значения (например . = 30000 или . = . + 100). Это выражение в объектном файле, который линкуется к другому объектному файлу, приведёт к неверному результату. Потому что точка имеет абсолютное значение и вычисляется сразу, а не во время линковки, и приводит к изменению значения текущего PC.

Примеры:

    START:  MOV   R2,R1
            BCS   .-2         ; переход на метку START.
            HALT
    ST:     MOV   PC,R1
            ADD   (PC)+,R1
            .WORD END-.+2

            MOV   PC,R1
            ADD   #END-.,R1   ;или прямо в строке команды
    END:    .END

Чтобы не использовать точку, этот текст можно записать в следующем виде:

    ST:     MOV   PC,R1
            ADD   (PC)+,R1
            .WORD @END+2
    END:    .END

Или можно использовать псевдокоманду .ADDR

            .ADDR R1,END

### Особенности реализации.

В арифметическом выражении можно использовать локальные метки, кроме присваиваний, в присваиваниях - нельзя. Видимость локальной метки ограничена глобальными, так что этим надо пользоваться с осторожностью, не стоит злоупотреблять.

Для совместимости со старым BKTurbo оставлен вариант деления метки на два такого вида: /MET, это полностью аналогично операции MET/2.

Для совместимости со старым BKTurbo в командах ветвления и SOB принято следующее допущение: если не используются скобки, то самый первый аргумент всегда интерпретируется как метка, даже если выглядит как число в синтаксисе Си. Однако число в формате MACRO-11 всё-таки опознаётся как число, из-за своего префикса '^'. Это сделано для того, чтобы без лишних телодвижений можно было компилировать тексты, в которых локальные метки были обычными числами.

Например:

BR 100 ; переход на метку 100, а не на адрес 100

BR <100> или BR ^o100 ; переход на абсолютный адрес 100

BR 0x100+4 ; переход на метку 0x100 плюс 4 байта

BR <100 + MET> ; переход на метку MET плюс 100 байтов.

В псевдокоманде .word можно задавать смещение к адресу, для этого служит символ @:

.word @met

Вместо met в общем случае может стоять арифметическое выражение:

.word @<arithm>

В результате будет рассчитано смещение до результата арифметического выражения. Однако, нельзя писать конструкции такого вида:

.word @met + @met2 ; это считается синтаксической ошибкой.

Но можно перечислять смещения через запятую:

.word @met, @met2 ; в этом случае всё вычисляется корректно.

1. Пример:

               .ADDR R0, START
               MOV  #140000, R1
               MOV  #/END - /START, R2
       1$:     MOV  (R0)+,(R1)+
               SOB  R2,1$
       START:  . . . . .
               . . . . .
       END:    .END

   Здесь массив от метки START до метки END пересылается на адрес 140000. Знак деления ( / ) означает, что адрес метки делится на 2, что в результате даёт в R2 длину массива в словах.

   Или современный вариант:

               .ADDR R0, START
               MOV  #140000, R1
               MOV  #<END - START>/2, R2
       1$:     MOV  (R0)+,(R1)+
               SOB  R2,1$
       START:  . . . . .
               . . . . .
       END:    .END

   Пересылка словами в два раза быстрее, чем байтами. Использование деления возможно везде, где возможно использовать арифметические выражения. По директиве .ADDR мы получаем в R0 абсолютный адрес метки START, даже если программа перемещаемая.

2. Запись байта числа в строку букв.

       CR = 12
       LF = 15
       .ASCII /ABC/<CR><LF> ; в строку дописать коды ПС,ВК

   В общем случае, в угловых скобках можно записать упрощённое арифметическое выражение, в котором отсутствуют скобки, и результат должен укладываться в байт, иначе будет ошибка переполнения и некорректный результат.

## МЕТОДЫ АДРЕСАЦИИ, КОМАНДЫ.

Методы адресации аналогичны системе команд ЭЛЕКТРОНИКИ-60.

Пример написания | Код адресации
--- | ---
CLR MET | 67
MOV #MET,R1 | 27
CLR @#MET | 37
CLR @MET | 77
CLR MET(R1) | 6n
CLR @MET(R1) | 7n
CLR R1 | 0n
CLR (R1) CLR @R1 | 1n
CLR -(R1) | 4n
CLR @-(R1) | 5n
CLR (R1)+ | 2n
CLR @(R1)+ | 3n

Обозначения в тексте:
ss | поле источника (6 бит)
dd | поле приёмника (6 бит)
R | регистр общего назначения (3 бита)
xx | смещение (8 бит), +127 .. -128
n | цифра (3 бита)
M | маска (4 бита)
nn | число (6 бит)
\ | резервный/неизвестный код
eis | EIS инструкция
fis | FIS инструкция
fpu | FPU инструкция
cis | CIS инструкция
src, S | операнд - источник
dst, D | операнд - приёмник
offset | смещение
CC | коды условий (биты в слове состояния процессора)
PC=R7=регистр | счётчик команд
SP=R6=регистр | указатель стека
PSW | слово состояния процессора
:: | каждое двоеточие - произвольная (восьмеричная) цифра
mmg | инструкция диспетчера памяти
si | специальная инструкция: уникальна для указанного процессора
Установки кодов условий:
- | не изменяется
+ | действия над битом в описании конкретной инструкции.
1 | всегда устанавливается в 1
0 | всегда очищается
* | устанавливается/чистится в соответствии с общими правилами: N - устанавливается, если старший бит результата установлен, иначе чистится; Z - устанавливается при нулевом результате, иначе чистится; V - устанавливается при арифметическом переполнении, иначе чистится;
C - устанавливается при переносе из старшего разряда, иначе очищается;

Двухоперандные:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
MOV[B] S,D | [1]1ssdd | * | * | 0 | - | пересылка: (dst) ← (src); MOVB ss,Rn (пересылка в регистр общего назначения), единственная среди байтовых инструкций, распространяет знаковый бит источника в старшем байте приёмника; при пересылке байта в регистр результат всегда пересылается в младший байт регистра. Все остальные байтовые пересылки работают с байтами в точности как словные со словами.
CMP[B] S,D | [1]2ssdd | * | * | + | + | сравнение src с dst и установка кодов условий PSW: (src) - (dst); V = 1, если операнды были разных знаков, и знак приёмника был тот же, что и знак результата (разности операндов), иначе чистится; C = 0, если был перенос из старшего разряда, иначе устанавливается.

Операция не изменяет исходных операндов.

BIT[B] S,D | [1]3ssdd | * | * | 0 | - | проверка бит dst по маске src и установка кодов условий: (src) & (dst)
BIC[B] S,D | [1]4ssdd | * | * | 0 | - | очистка бит dst по маске src: (dst) ← ~(src) & (dst)
BIS[B] S,D | [1]5ssdd | * | * | 0 | - | установка бит dst по маске src: (dst) ← (src) | (dst)
XOR Rn,D | 074Rdd | * | * | 0 | - | (eis) исключающее 'или': (dst)← R xor (dst)
ADD S,D | 06ssdd | * | * | + | * | (dst) ← (src) + (dst); V устанавливается, если оба операнда были одного знака, а результат - противоположного, иначе - чистится
SUB S,D | 16ssdd | * | * | + | + | (dst) ← (dst) - (src); V устанавливается, если операнды были различных знаков, а знак источника был таким же, как и знак результата, иначе чистится; C чистится, если был перенос из старшего разряда, иначе - устанавливается

Однооперандные:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
CLR[B] D | [1]050dd | 0 | 1 | 0 | 0 | обнуление приёмника: (dst) ← 0 Перед обнулением, как и для всех однооперандных команд, происходит чтение из приёмника
COM[B] D | [1]051dd | * | * | 0 | 1 | побитовое инвертирование операнда: (dst) ← ~(dst)
INC[B] D | [1]052dd | * | * | + | - | прибавление 1 к операнду: (dst) ← (dst) + 1; V устанавливается если превышает 077777, иначе чистится
DEC[B] D | [1]053dd | * | * | + | - | вычитание 1 из операнда: (dst) ← (dst) - 1; V устанавливается, если (dst) было 100000, иначе чистится.
NEG[B] D | [1]054dd | * | * | + | + | заменяет источник его дополнением до 1 (меняет знак числа): (dst) ← -(dst); V устанавливается, если результат стал 100000, иначе чистится; C чистится, если получен результат 0, иначе устанавливается
ADC[B] D | [1]055dd | * | * | + | + | прибавление к операнду бита переноса (для длинной арифметики): (dst) ← (dst) + C V устанавливается, если (dst) был 077777 и C был установлен, иначе чистится; C устанавливается, если (dst) был 177777 и C был установлен, иначе чистится
SBC[B] D | [1]056dd | * | * | + | + | вычитание из операнда бита переноса (для длинной арифметики): (dst) ← (dst) – C V = 1, если (dst) был 100000 и C был установлен, иначе чистится; C = 1, если (dst) был 0, и C был установлен, иначе чистится
TST[B] D | [1]057dd | * | * | 0 | 0 | установка битов условий PSW, соответствующих операнду
ROR[B] D | [1]060dd | * | * | + | + | кольцевой сдвиг вправо: вращает все биты операнда на одну позицию вправо; старший бит загружается из бита переноса, младший бит, выдвинутый из операнда, загружается в бит переноса; V = C xor N.
ROL[B] D | [1]061dd | * | * | + | + | кольцевой сдвиг влево: вращает все биты операнда на одну позицию влево; младший бит грузится из бита переноса, выдвинутый старший бит загружается в бит переноса; V = C xor N.
ASR[B] D | [1]062dd | * | * | + | + | арифметический сдвиг вправо: содержимое операнда сдвигается на одну позицию вправо; старший (знаковый) бит остаётся неизменным; бит переноса грузится содержимым выдвинутого бита; V = C xor N. Операцию можно трактовать как целочисленное деление операнда пополам, с остатком, остающимся в бите переноса.
ASL[B] D | [1]063dd | * | * | + | + | арифметический сдвиг влево: содержимое операнда сдвигается на одну позицию влево, младший бит чистится, выдвинутый знаковый бит грузится бит переноса; V = C xor N. Операцию можно рассматривать как целочисленное умножение операнда на 2.
SWAB D | 0003dd | + | + | 0 | 0 | обмен байтов в слове: byte1/byte0 ← byte0/byte1 N, Z устанавливаются по младшему байту результата
SXT D | 0067dd | - | + | 0 | - | (eis) распространение знака: (dst) ← 0 если бит N очищен (dst) ← 177777 если бит N установлен Z устанавливается, если бит N очищен
MTPS S | 1064ss | + | + | + | + | запись в слово состояния: PSW ← ss
MFPS D | 1067dd | - | - | - | - | чтение слова состояния: (dst) ← PSW
PUSH[B] S | [1]1ss46 | * | * | 0 | - | это команда MOV[B] ss, -(SP),
POP[B] D | [1]126dd | * | * | 0 | - | это команда MOV[B] (SP)+, dd

Ветвления:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
BR MET | 0004xx | - | - | - | - | переход безусловный. Смещение xx автоматически умножается на 2, и складывается с PC: PC ← PC + (2*offset)
BNE MET | 0010xx | - | - | - | - | переход по не нулю: PC ← PC + (2 * offset) if Z = 0
BEQ MET | 0014xx | - | - | - | - | переход по нулю: PC ← PC + (2 * offset) if Z = 1
BGE MET | 0020xx | - | - | - | - | переход по больше или равно: PC ← PC + (2 * offset) if N xor V = 0
BLT MET | 0024xx | - | - | - | - | переход по меньше: PC ← PC + (2 * offset) if N xor V = 1
BGT MET | 0030xx | - | - | - | - | переход по больше: PC ← PC + (2 * xx) if Z | (N xor V) = 0
BLE MET | 0034xx | - | - | - | - | переход по меньше или равно: PC ← PC + (2 * xx) if Z | (N xor V) = 1
BPL MET | 1000xx | - | - | - | - | переход, если плюс: PC ← PC + (2 * offset) if N = 0
BMI MET | 1004xx | - | - | - | - | переход, если минус: PC ← PC + (2 * offset) if N = 1
BHI MET | 1010xx | - | - | - | - | переход, если выше: PC ← PC + (2 * offset) if C & Z = 0
BLOS MET | 1014xx | - | - | - | - | переход, если ниже или равно: PC ← PC + (2 * offset) if C | Z = 1
BVC MET | 1020xx | - | - | - | - | переход, если нет переполнения: PC ← PC + (2 * offset) if V = 0
BVS MET | 1024xx | - | - | - | - | переход, если переполнение: PC ← PC + (2 * offset) if V = 1
BCC MET | 1030xx | - | - | - | - | переход, если не было переноса: PC ← PC + (2 * offset) if C = 0
BCS MET | 1034xx | - | - | - | - | переход, если был перенос: PC ← PC + (2 * offset) if C = 1
BHIS MET | 1030xx | - | - | - | - | переход, если выше или равно: PC ← PC + (2 * offset) if C = 0
BLO MET | 1034xx | - | - | - | - | переход, если ниже: PC ← PC + (2 * offset) if C = 1

Управления:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
JMP D | 0001dd | - | - | - | - | Переход. Грузит PC из источника, используя адресацию dd. Код адресации 0 (попытка выполнить команду, записанную в регистре общего назначения) на большинстве типов процессоров приводит к TRAP TO 4; PC ← (dst)
SOB Rn,MET | 077Rnn | - | - | - | - | (eis) R ← R - 1; если не 0, то PC ← PC-2*offset
JSR Rn,D | 004Rdd | - | - | - | - | переход на подпрограмму: -(SP) ← R, R ← PC, PC ← (dst)
RTS Rn | 00020R | - | - | - | - | Возврат из подпрограммы. PC ← R, R ← (SP)
MARK n | 0064nn | - | - | - | - | чистка стека: SP ← PC + 2*nn, PC ← R5, R5 ← (SP)+; nn - число параметров.
TRAP n | 104000-104377 | + | + | + | + | -(SP) ← PSW; -(SP) ← PC; PC ← (34); PSW ← (36) N,Z,V,C: грузятся из вектора прерывания
EMT n | 103400-103777 | + | + | + | + | -(SP) ← PSW; -(SP) ← PC; PC ← (30); PSW ← (32) N,Z,V,C: грузятся из вектора прерывания
NOP | 000240 | - | - | - | - | Ничего не делает, точнее, чистит никакие биты условий в PSW (M=0, см. таблицу команд условий).
BPT | 000003 | + | + | + | + | Отладочное прерывание по 14 ячейке. -(SP) ← PSW, -(SP) ← PC, PC ← (14), PSW ← (16) N,Z,V,C: загружаются из вектора прерывания
IOT | 000004 | + | + | + | + | Программное прерывание ввода-вывода по 20 ячейке. -(SP) ← PSW, -(SP) ← PC, PC ← (20), PSW ← (22); N,Z,V,C грузятся из вектора прерывания
WAIT | 000001 | - | - | - | - | Инструкция ожидания прерывания. Процессор останавливается и освобождает системную магистраль до первого незамаскированного прерывания. PC указывает на следующую за WAIT команду. После возврата из прерывания выполнение программы продолжается со следующей за WAIT команды.
RESET | 000005 | - | - | - | - | Сброс внешних устройств. Процессорами, имеющими диспетчер памяти, в моде USER / SUPERVISOR исполняется как NOP. Выставляет сигнал сброса внешних устройств на магистраль. Время выполнения зависит от типа процессора и его тактовой частоты.
HALT | 000000 | - | - | - | - | Выводит процессор из активного режима: прерывается выполнение команд текущей программы, PC указывает на следующую за прерванной команду, PC отображается на консоли, разрешена работа консоли. На разных процессорах реализована различно: 1801ВМ1,2,3, 1811 переводит в программу обслуживания останова.

Для 1801ВМ1 (177716) ← 000010 | (177716), (177676) ← (PSW), (177674) ← (PC), (PC) ← (160002), (PSW) ← (160004);

* Здесь и далее адреса системных регистров приводятся для главного процессора; предполагается, что системное ПЗУ расположено с адреса 160000.

Для ВМ2 в пользовательском режиме вызывает прерывание в моду HALT по вектору останова, то же в моде HALT при разрешённом HALT, при запрещённых выполняется как NOP. В других процессорах: если нет диспетчера памяти, и процессор соответствует старым стандартам DEC, процессор останавливается, и может быть перезапущен с точки останова, либо с набранного с пульта адреса пультовым переключателем; если есть диспетчер памяти, и команда выполнялась не в моде KERNEL, то происходит прерывание и переход в моду KERNEL по резервной инструкции; в моде KERNEL останавливает процессор, если он не 1801/06 ВМ3. Для них HALT в моде KERNEL переводит процессор в моду HALT: стек пультовой моды устанавливается с адреса 100000, далее в этот стек заносятся PC, PSW; загружает PSW константой 340, включает диспетчер памяти на преобразование адресов в 22-разрядные и стартует с адреса 0 пультового ОЗУ (скрытого от пользователя).

RTI | 000002 | + | + | + | + | Возврат из прерывания. PC ← (SP)+, PSW ← (SP)+, грузит все доступные биты слова состояния из стека
RTT | 000006 | + | + | + | + | (eis) Аналогична RTI, но при установленном бите T в PSW прерывание после неё не возникает до окончания следующей команды.
CALL D | 0047dd | - | - | - | - | это команда JSR PC,dd
RETURN | 207 | - | - | - | - | это команда RTS PC

Условия:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
CL<NZVC> | 00024M | + | + | + | + | чистит биты PSW (N,Z,V,C) по маске из 4 младших битов кода команды.
CLC | 000241 | + | + | + | + | (M = ^B 0001)
CLV | 000242 | + | + | + | + | (M = ^B 0010)
CLVC | 000243 | + | + | + | + | (M = ^B 0011)
CLZ | 000244 | + | + | + | + | (M = ^B 0100)
CLZC | 000245 | + | + | + | + | (M = ^B 0101)
CLZV | 000246 | + | + | + | + | (M = ^B 0110)
CLZVC | 000247 | + | + | + | + | (M = ^B 0111)
CLN | 000250 | + | + | + | + | (M = ^B 1000)
CLNC | 000251 | + | + | + | + | (M = ^B 1001)
CLNV | 000252 | + | + | + | + | (M = ^B 1010)
CLNVC | 000253 | + | + | + | + | (M = ^B 1011)
CLNZ | 000254 | + | + | + | + | (M = ^B 1100)
CLNZC | 000255 | + | + | + | + | (M = ^B 1101)
CLNZV | 000256 | + | + | + | + | (M = ^B 1110)
CCC | 000257 | + | + | + | + | (M = ^B 1111)
SE<NZVC> | 00026M | + | + | + | + | устанавливает биты PSW (N,Z,V,C) по маске из 4 младших битов кода команды.
SEC | 000261 | + | + | + | + | (M = ^B 0001)
SEV | 000262 | + | + | + | + | (M = ^B 0010)
SEVC | 000263 | + | + | + | + | (M = ^B 0011)
SEZ | 000264 | + | + | + | + | (M = ^B 0100)
SEZC | 000265 | + | + | + | + | (M = ^B 0101)
SEZV | 000266 | + | + | + | + | (M = ^B 0110)
SEZVC | 000267 | + | + | + | + | (M = ^B 0111)
SEN | 000270 | + | + | + | + | (M = ^B 1000)
SENC | 000271 | + | + | + | + | (M = ^B 1001)
SENV | 000272 | + | + | + | + | (M = ^B 1010)
SENVC | 000273 | + | + | + | + | (M = ^B 1011)
SENZ | 000274 | + | + | + | + | (M = ^B 1100)
SENZC | 000275 | + | + | + | + | (M = ^B 1101)
SENZV | 000276 | + | + | + | + | (M = ^B 1110)
SCC | 000277 | + | + | + | + | (M = ^B 1111)

Расширенной арифметики:

Команда | Мнемоника | Флаги | Описание
--- | --- | --- | ---
N | Z | V | C
MUL S,Rn | 070Rss | * | * | 0 | + | (eis) Содержимое регистра назначения, и источника, рассматриваемые как целые числа в дополнительном коде, перемножаются и запоминаются в регистре - приёмнике, и следующем (по номеру) регистре, если номер регистра - приёмника чётный. Если номер регистра нечётный, запоминается только младшая часть произведения.

Rn:R(n|1) = Rn * (SS)

В Rn: содержится старшая часть результата, в R(n|1) - младшая.

DIV S,Rn | 071Rss | + | + | + | + | (eis) 32-битовое целое в дополнительном коде, находящееся в регистрах Rn:R(n|1), делится на значение операнда - источника. Деление будет произведено так, что остаток будет одного знака с делимым. Номер регистра должен быть чётным. N устанавливается, если результат отрицателен, иначе чистится;

Z устанавливается, если результат нулевой, иначе чистится;

V устанавливается, если делимое равно нулю, либо если значение регистра по модулю больше абсолютной величины делителя: в этом случае деление не выполняется, поскольку результат выйдет за разрядную сетку.

C устанавливается, если делитель равен нулю, иначе очищается.

В Rn: содержится старшая часть делимого, в R(n|1) - младшая.

Rn = Rn:R(n|1) \ (SS) - целая часть деления

R(n|1) = Rn:R(n|1) % (SS) - остаток от деления

ASH S,Rn | 072Rss | * | * | + | + | (eis) Содержимое регистра сдвигается вправо или влево на число позиций, указанное в операнде - источнике. Счётчиком позиций являются 6 младших бит источника. Отрицательное значение счётчика сдвига означает сдвиг вправо, положительное - влево. При правом сдвиге распространяется старший бит, при левом - младший грузится нулём.

V устанавливается, когда знак регистра менялся во время операции, иначе чистится;

C загружается последним битом, выдвинутым из регистра.

ASHC S,Rn | 073Rss | * | * | + | + | (eis) Содержимое регистра, и регистра с номером, полученным установкой младшего бита в номере указанного регистра, рассматривается как одно 32 - битовое слово, причём в регистре с большим номером содержатся младшие биты, а регистре с меньшим - старшие, сдвигается влево или вправо на число позиций, указанное счётчике сдвига. Счётчиком сдвига являются младшие 6 бит операнда - источника. Отрицательное значение счётчика вызывает сдвиг вправо, положительное - влево. Если номер регистра - приёмника нечётный, правый сдвиг становится вращением. 16-разрядное слово вращается вправо на число позиций, указанное в счётчике сдвига. При правом сдвиге распространяется старший бит, при левом сдвиге младший бит грузится нулём.

V устанавливается, если при сдвиге изменялся знак операнда, иначе чистится;

C загружается последним выдвинутым из 32-битового операнда битом.

FADD Rn | 07500R | * | * | 0 | 0 | (fis)

initial state:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | operand A bits 16-31
(R)+6 | => | operand A bits 0-15
After operation floating point stack looks:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | result bits 16-31
|  | result bits 0-15
result = A+B

FSUB Rn | 07501R | * | * | 0 | 0 | (fis)

initial state:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | operand A bits 16-31
(R)+6 | => | operand A bits 0-15
After operation floating point stack looks:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | result bits 16-31
|  | result bits 0-15
result = A-B

FMUL Rn | 07502R | * | * | 0 | 0 | (fis)

initial state:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | operand A bits 16-31
(R)+6 | => | operand A bits 0-15
After operation floating point stack looks:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | result bits 16-31
|  | result bits 0-15
result = A*B

if the result < 2E-128 then the result is treated as zero.

FDIV Rn | 07503R | * | * | 0 | 0 | (fis)

initial state:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | operand A bits 16-31
(R)+6 | => | operand A bits 0-15
After operation floating point stack looks:
R | => | operand B bits 16-31
|  | operand B bits 0-15
|  | result bits 16-31
|  | result bits 0-15
result = A/B

if the result < 2E-128 then the result is treated as zero.

ПРИМЕЧАНИЕ: Нельзя использовать относительную адресацию (67,77) при обращении к внешним, по отношению к транслируемой программе, адресам при трансляции в перемещаемом формате. Нужно использовать абсолютную адресацию.

Т.е. использовать то можно, но работать такая программа не будет, если её загрузить не с адреса компиляции.

Примеры:

    MOV   R0,@#177714 ;Ограничений нет.
    MOV   R0,177714   ;Можно транслировать только в абсолютном формате (CO).

Если нужно транслировать по CL, то можно сделать таким образом:

    PORT=177714 ;Перед началом программы.
    . . . .
    . . . .
    MOV R0,PORT

## ПСЕВДОКОМАНДЫ.

.LA выражение синоним .LINK выражение | - псевдокоманда, задающая адрес начала трансляции неперемещаемой программы, эта команда должна быть самой первой командой. Её допустимо подавать после начальных присвоений.
.BLKB [выражение] | - резервирование количества байт, которое получается в результате вычисления выражения. В выражении нельзя использовать неопределённых меток и определения. Потому что выражение вычисляется сразу и изменяет текущий счётчик указателя команд. Если выражение остутствует, то принимается значение 1, т.е. резервируется один байт.
.BLKW [выражение] | - резервирование количества слов, которое получается в результате вычисления выражения. Если выражение остутствует, то принимается значение 1, т.е. резервируется одно слово.
| ПРИМЕЧАНИЕ: Если аргумент этих псевдокоманд слишком велик, то возможно сообщение об ошибке (см. в начале описания). В качестве аргумента можно использовать арифметическое выражение, в составе которого есть имена переменных и метки, значения которых должны быть определены на момент вычисления выражения, иначе будет ошибка 112.

Потому что выражение вычисляется сразу и изменяет текущий счётчик указателя команд.

.WORD [N][,[N],...,[N]] | - где N - допустимое арифметическое выражение. Запись слова (число или имя). Если нужно записать смещение к метке, то это делается (MET-.) или (@MET), что означает из адреса метки вычесть текущее значение счётчика. Операнды разделяются запятыми.

Если операнд пустой, т.е. отстутствует, то принимается значение 0.

Примеры:

    .WORD 1, -1 , @START, START - . , START + STOP - END, 'A, "BC

    .WORD

эквивалентно

    .WORD 0

а

    .WORD ,,

эквивалентно

    .WORD 0,0,0

.BYTE [N][,[N],...,[N]] | - где N - допустимое арифметическое выражение. Запись байта (число).
    .BYTE 1, -177776, 'A, 'B, A$END-A$BGN, BLKLEN
При записи числа надо учитывать, что, например, -1=177777 и в байт это число записать нельзя, а -177776=2 и в байт записать можно. То же касается арифметических выражений, если результат умещается в байт, то всё нормально, иначе - ошибка. Если операнд пустой, т.е. отстутствует, то принимается значение 0.
.EVEN | - если содержание счётчика команд нечётное, то добавляется нулевой байт для выравнивания счётчика до чётного значеения, иначе игнорируется.
.ASCII /.../ | - запись строки символов в память. Строка ограничивается кворируемыми символами. (символами с кодами 041..057, 072..0100, 0133..0177, 0173..0177). Квотируемые символы в начале и конце строки должны быть одинаковыми, и естественно, этих символов не должно быть внутри строки. Символы в числовой форме заключается в угловые скобки.

Допустимые квотируемые символы, которые можно использовать: !"#$%&'()*+,-./:=?@[\]^_{|}~ - все знаки пунктуации и всякие такие символы, которые не буквы и не цифры. Вот всё это и можно использовать, а не только кавычки и косую черту.

Пример:

    .ASCII /ВЫХОД В МОНИТОР ?/<40><40>"Да/Нет:"<12>

В первом случае для ограничения строки использована дробная черта, во втором, поскольку она есть в строке, использованы кавычки.

Вместо числа в угловых скобках можно использовать простые арифметические выражения без скобок.

.ASCIZ /.../ | - то же, с добавлением в конце строки нулевого байта.
.RAD50 /.../ | - запись строки в коде RADIX-50 в память, причём в поле операнда допустимо наличие пробелов. Квотируемые символы те же самые, что и для .ascii, и так же кроме угловых скобок.
    .RAD50 /HALT/
.PRINT #TXT | - расшифровывается как:
    MOV #TXT,R1
    CLR R2
    EMT 20
.ADDR Rn,МЕТ | - получение в регистре Rn абсолютного адреса метки МЕТ, расшифровывается как:
    MOV   PC,Rn
    ADD   (PC)+,Rn
    .WORD @MET+2
Пример:
    START:  .ADDR R1,TEXT
            CLR R2
            EMT 20
            HALT
    TEXT:   .ASCIZ <234>/ERROR !/<234>
            .EVEN
Аналогично .PRINT #TEXT, но .PRINT #TEXT нельзя использовать в перемещаемой программе.
.ORG выражение | - выравнивает текущий указатель адреса по результату выражения. Допускается нечётное значение. Если результат меньше текущего указателя - ничего не происходит. Внимание! Эта псевдокоманда неправильно работает в объектных модулях. Т.е. её нельзя применять, если планируется компилировать код в режиме CL, и прилинковывать его к другому модулю.

Она как и .blkb(w) сразу выполняет операцию выравнивания, а не во время линковки. В результате при линковке объектного модуля с этой псевдокомандой получается полная фигня.

Выражение может отсутствовать, при этом команда не делает ничего.

.ALIGN выражение | - выравнивание по границе блока, являющегося степенью двойки. Это как расширенная псевдокоманда .EVEN. Выражение - это упрощенное арифметическое выражение, в котором не допустимы скобки, неопределённые метки и присваивания и локальные метки. Если аргумент команды отсутствует или равен 0, то команда ничего не делает.

Если аргумент равен 1, то результат - выравнивание по нечётному адресу, это делала бы псевдокоманда .ODD, но она в принципе не нужна на БК.

Если аргумент равен 2, то результат аналогичен псевдокоманде .EVEN - выравнивание по чётному адресу.

Остальные результаты вычисления выражения интерпретируются так: у числа определяется старший значимый бит, остальные обнуляются. Т.е. получается приведение к степени двойки в меньшую сторону. Это сделано для того, чтобы не заморачиваться с определением, является ли аргумент степенью двойки.

Максимальное значение - 32767, что приводится к числу, степени двойки 16384, слишком большое число вызывает ошибку 136.

.FLT2 число | - упаковка текстового представления числа с плавающей запятой в два слова. Число может быть задано в обычной и экспоненциальной форме. Пригодно для использования с инструкциями FIS.
.FLT4 число | - упаковка текстового представления числа с плавающей запятой в четыре слова. Число может быть задано в обычной и экспоненциальной форме. Пригодно для использования с инструкциями математического сопроцессора 1801ВМ4.
.INCLUDE "имя файла" | - в исходный код включается код из заданного файла. Особенности:

- Именем файла считается вся строка символов после псевдокоманды, конец строки - начало комментария или пробельные символы, или символ конца строки.
- Имя файла можно не заключать в кавычки, если оно не содержит пробелов, иначе - кавычки обязательны. В этом случае, ограничителем строки считается заключительная кавычка, и в этой же строке можно располагать ещё одну команду.
- Поиск файла начинается с директории, в которой находится файл, содержащий псевдокоманду .INCLUDE, поэтому допускается задавать относительный путь. Например:

.INCLUDE "inc/parameters.asm"

Кавычки здесь - любой квотируемый символ, описанный в псевдокдманде .ascii, так что, если имя вашего файла начинается с такого символа, придётся заключать его в другие квотируемые символы, иначе будет ошибка - не найдена заключающая кавычка.

.INSERT "имя файла" | - в генерируемый бинарный файл в том месте, где встретилась эта псевдокоманда, вставляется содержимое файла, заданного аргументом, как обычный массив байтов. Полезна для вставки ресурсов данных непосредственно при компиляции. Чтобы не использовать утилиту Bin2Obj и не линковать потом множество объектных файлов.

Синтаксис аргумента полностью аналогичен команде .INCLUDE. И в плане квотируемых символов - то же.

Sprite: .INSERT "res/ufo.spr"

.SCRIPT "имя скрипта" текст скрипта .ENDS | - задание скрипта, который будет выполнен после окончательной линковки, перед записью результата на диск, с целью модификации бинарного файла без использования сторонних средств. Особенности:

- Имя скрипта опционально. Его можно не задавать, оно нужно лишь для того, чтобы видеть, какой из нескольких скриптов выполняется в данный момент. И если возникнет ошибка, то, в каком из них она произошла.
- Именем скрипта считается вся строка символов после псевдокоманды, конец строки - начало комментария или пробельные символы, или символ конца строки.
- Имя скрипта можно не заключать в кавычки, если оно не содержит пробелов, иначе - кавычки обязательны. В этом случае, ограничителем строки считается заключительная кавычка, и в этой же строке можно располагать ещё одну команду.
- Кавычки - это любой квотируемый символ, описанный в псевдокдманде .ascii.
- Скриптом считается вся последовательность строк, пока не встретится псевдокоманда .ENDS.

.ENABL | Команда заставляет транслировать операнды с адресацией 67 как 37.
.DSABL | Отмена действия псевдокоманды .ENABL.
.END | Оператор завершения программы.

## Описание работы со скриптами.

### Что такое скрипт и зачем он нужен.

Скрипт - это текст, заключённый между псевдокомандами .SCRIPT "Имя скрипта" и .ENDS, компилятор при разборе ассемблерного текста, сохраняет все строки между этими псевдокомандами в отдельный массив, не проводя синтаксического анализа. Анализ делается позже, скриптовым интерпретатором при выполнении скрипта.

Выполнением скрипта занимается простой интерпретатор, поэтому интеллектуальные возможности его довольно ограничены. Он выдаёт ошибки, если чего-то не хватает в рамках синтаксиса, но если есть что-то лишнее, то он просто молча это игнорирует.

Скриптов в тексте может быть неограниченное количество. Все они считаются автономными единицами, никак не связанными и не взаимодействующими между собой. Располагаться скрипты могут в любом месте программы до псевдокоманды .END.

Скрипты выполняются после окончательной компиляции или линковки, перед сохранением результирующего файла. Их назначение - модификация исполняемого файла без применения сторонних средств. Например - подсчёт КС и подстановка результата в заданное место в программе. Или кодирование/шифрование определённого участка программы.

Выполняются скрипты последовательно друг за другом в том порядке, в каком они располагаются в исходном тексте. На экран выводятся информационные сообщения о том, какой скрипт выполняется и о результатах выполнения, а так же ошибки. Выполнение скрипта прекращается при первой же встреченной ошибке, и переходит к следующему, если он есть.

При компиляции командой CL скрипты сохраняются в объектном файле, чтобы быть применёнными при линковке объектных модулей. При компиляции командой CO скрипты не сохраняются в создаваемом объектном файле, а применяются сразу.

### Формат скрипта.

Скрипт представляет собой набор ассемблерных команд, перед командой может быть метка. В строке может быть только одна команда и только одна метка. Допускаются пустые строки, они сохраняются как строка скрипта, но при выполнении игнорируются и просто занимают место. Допускается метка, в строке без команды.

Меткой считается лексема, состоящая из букв, цифр, знаков '_' и '$' и заканчивающаяся двоеточием. Метки скрипта никак не пересекаются с метками основного ассемблерного текста. Проверки на дублирующиеся метки не делается, если вы это допустите, ваш скрипт просто будет неправильно интерпретироваться и только. Потому что в командах ветвления и цикла производится поиск заданной метки с начала скрипта. И берётся первая найденная метка, совпадающая с меткой перехода.

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

Имеется ограниченный набор команд скрипта, частично повторяющий набор команд PDP-11: двухоперандных, однооперандных, команд ветвления и команды цикла SOB.

Так же есть дополнительный набор команд: работа с внешними файлами и вывод текста на экран.

Стека нет, внутренней памяти нет, следовательно, подпрограмм тоже нет. Есть набор восьми 16 разрядных регистров R0..R7, равноправных между собой. Если этого будет не хватать, то количество регистров можно увеличить, попросив меня об этом. Набор адресаций отличается от стандартного PDP-11.

Арифметических выражений нет, скрипт их просто не понимает, и числа - только восьмеричные, никаких других систем счисления нет.

### Команды скрипта.

#### Двухоперандные команды.

MOV(B) | ss, dd | пересылка
CMP(B) | ss, dd | сравнение
BIT(B) | ss, dd | тестирование битов
BIC(B) | ss, dd | очистка битов
BIS(B) | ss, dd | логическое ИЛИ
AND(B) | ss, dd | логическое И
XOR(B) | ss, dd | логическое исключающее ИЛИ (может быть байтовым)
ADD(B) | ss, dd | сложение (может быть байтовым)
SUB(B) | ss, dd | вычитание (может быть байтовым)
XCHG | ss, dd | обмен словами между источником и приёмником

#### Однооперандные команды.

SWAB | dd | обмен байтами в слове
CLR(B) | dd | очистка
COM(B) | dd | инверсия
INC(B) | dd | инкремент
DEC(B) | dd | декремент
NEG(B) | dd | смена знака
ADC(B) | dd | сложение с битом С
SBC(B) | dd | вычитание бита С
TST(B) | dd | тестирование
ROR(B) | dd | циклически сдвиг вправо
ROL(B) | dd | циклически сдвиг влево
ASR(B) | dd | арифметический сдвиг вправо
ASL(B) | dd | арифметический сдвиг влево
RCR(B) | dd | циклический сдвиг вправо без охвата бита С (как на x86)
RCL(B) | dd | циклический сдвиг влево без охвата бита С (как на x86)

#### Команды ветвления.

BR | label | безусловный переход
BNE | label |
BEQ | label |
BGE | label |
BLT | label |
BGT | label |
BLE | label |
BPL | label |
BMI | label |
BHI | label |
BLOS | label |
BVC | label |
BVS | label |
BCC (BHIS) | label |
BCS (BLO) | label |

Все одноперандные и двухоперандные команды изменяют флаги NZVC в соответствии с логикой PDP-11. Все команды ветвления работают соответственно.

#### Команда цикла

SOB | Rn, label | Переход к метке, если --Rn не равно нулю.

Эта команда может делать переход вперёд, если label будет метка, заданная после команды SOB, скрипту всё равно, он не считает это ошибкой.

#### Команды установки/сброса признаков

STF | NZVC | установка
CLF | NZVC | сброс

Устанавливаются/сбрасываются перечисленные после команды флаги-признаки. Если за командой ничего не будет, то команда ничего не сделает. Это аналогично команде NOP, которая в скриптах бессмысленна.

Флаги можно записывать в любом порядке, пробелы между ними запрещены. Любой символ, кроме N,Z,V,C, считается концом перечисления. Т.е. можно писать строку неограниченной длины, состоящую из этих символов, что глупо, но допустимо.

#### Адресации

Есть 7 адресаций:

0 | Rn | - регистровая аргумент - содержимое регистров R0..R7
1 | (Rn) | - косвенно-регистровая. аргумент - значение по адресу в регистрах R0..R7
2 | (Rn)+ | - автоинкрементная аргумент - значение по адресу в регистрах R0..R7, после чего значение регистра увеличивается на 2 или 1, в зависимости от типа команды: словная или байтовая
3 | -(Rn) | - автодекрементная значение по адресу в регистрах R0..R7, сначала уменьшается на 2 или 1, в зависимости от типа команды: словная или байтовая, затем значение используется как адрес аргумента
4 | Rx[Ry] | - индексная, базами индекса (Rx) могут быть только 4 регистра R4..R7, индексами (Ry) - все 8 аргумент - значение по адресу, вычисляемому для байтовой адресации как Rx + Ry, а для словной - как Rx + 2 * Ry
5 | #label или #Число | - непосредственная значение - имя реальной глобальной метки из ассемблерного кода. Это имя ищется в таблице глобальных меток и если находится там, то аргументом считается значение метки. Если не находится, то возникает ошибка выполнения скрипта и выполнение прекращается.

или

Восьмеричное число, записываемое в дополнительном коде, отрицательных чисел нет. Это число не может использоваться как адрес.

Эта адресация бессмысленна в качестве приёмника. Потому что интерпретатор скрипта сохраняет значение в никуда. Оно просто теряется.

Так же эта адресация бесполезна в качестве источника в команде XCHG, она превращает её в обычный MOV.

6 | label или Число | - абсолютная адресация значение - имя реальной глобальной метки из ассемблерного кода. Это имя ищется в таблице глобальных меток и если находится там, то аргументом считается значение по адресу метки. Если не находится, то возникает ошибка выполнения скрипта и выполнение прекращается.

или

Восьмеричное число, записываемое в дополнительном коде, отрицательных чисел нет. Это число интерпретируется как абсолютный адрес ячейки памяти. Например, если программа компонуется с адреса 1000, и вы точно знаете, где, что находится, то можно вместо меток записывать абсолютные адреса в программе.

Эта адресация применяется чтобы прочитать/записать/изменить значение по конкретному адресу скомпилированной программы.

TODO: а может сделать индексную автоинкрементную/автодекрементную? Пока не знаю.

#### Дополнительные команды

Дополнительные команды отличаются от ассемблерных принципом функционирования, поэтому, чтобы было явно заметно, что это команда другого типа, все они начинаются со знака '$'.

#### Команды для работы с файлами

Группа команд предназначена для того, чтобы можно было скриптом читать данные из внешнего файла, или записывать во внешний файл.

Одновременно может быть открыто 8 файлов. Пока так, пока что я считаю что и этого много.

Файл представляет собой устройство с последовательным доступом. Позиционирования нет, чтобы подвести указатель файла к определённому месту, нужно прочитать заданные количество байтов/слов.

$open | #n, "имя файла" | открыть файл для чтения. аргументы: #n, n=0..7 - номер слота открываемого файла, если слот уже занят, т.е. файл в этом слоте уже открыт, то ошибка, устанавливается бит С в PSW. "имя файла" - имя открываемого файла, Нет никаких путей, файл должен находиться в текущей директории, там же, где создаётся компилируемый бинарник.

Если такого файла нет, или его не удаётся открыть по каким-то причинам, то ошибка, устанавливается бит С в PSW.

$create | #n, "имя файла" | создать и открыть файл для записи. аргументы: #n, n=0..7 - номер слота создаваемого файла, если слот уже занят, т.е. файл в этом слоте уже открыт, то ошибка, устанавливается бит С в PSW. "имя файла" - имя открываемого файла, Нет никаких путей, файл должен находиться в текущей директории, там же, где создаётся компилируемый бинарник.

Если файл не удаётся создать или открыть по каким-то причинам, то ошибка, устанавливается бит С в PSW.

Существующий файл перезаписывается.

$close | #n | закрыть открытый файл. аргументы - #n, n=0..7 - номер слота закрываемого файла, если файл уже закрыт, то ничего не делается, ошибки нет.
$get | #n | прочитать слово из файла, результат помещается в R0. аргументы - #n, n=0..7 - номер слота файла. Если файл не открыт, то R0 не изменяется, устанавливается бит С в PSW, если файл закончился, попытка чтения за концом файла - R0 = 0, устанавливается бит С и V в PSW.
$getb | #n | прочитать байт из файла, результат помещается в младший байт R0 с расширением знака. аргументы - #n, n=0..7 - номер слота файла. Если файл не открыт, то R0 не изменяется, устанавливается бит С в PSW, если файл закончился, попытка чтения за концом файла - R0 = 0, устанавливается бит С и V в PSW.
$put | #n | записать слово из R0 в файл. аргументы - #n, n=0..7 - номер слота файла. Если файл не открыт, устанавливается бит С в PSW.
$putb | #n | записать младший байт из R0 в файл. аргументы - #n, n=0..7 - номер слота файла. Если файл не открыт, устанавливается бит С в PSW.

Для имени файла действуют те же правила, что и для текстового аргумента псевдокоманды .include - имя может быть без кавычек (квотируемых символов), если в нём нет пробелов. Набор квотируемых символов тот же, что и для псевдокоманды .ascii.

При смешивании байтовых и словных операций чтения/записи нужно самостоятельно следить за тем, чтобы слова читались/записывались по чётным адресам, если это необходимо.

#### Команды для вывода текста

Группа команд предназначена для вывода на экран дополнительных информационно-диагностических сообщений из скрипта.

$type | "строка текста" | вывод на экран указанной строки.

Для строки текста действуют те же правила, что и для текстового аргумента псевдокоманды .include - строка может быть без кавычек (квотируемых символов), если в ней нет пробелов, т.е. одно слово можно не заключать в кавычки, а предложение - нужно заключать обязательно. Набор квотируемых символов тот же, что и для псевдокоманды .ascii.

После вывода текстовой строки автоматически делается перевод курсора на новую строку.

Так же команда $type может вывести на экран содержимое какого-либо адреса или значение метки.

Но у неё уже совсем другой формат.

$type | ("строка {:}", аргумент) | вывод на экран указанной строки и значения аргумента.

Как видно, параметры команды должны быть заключены в круглые скобки - это признак того, что за строкой находится второй аргумент, значение которого нужно показать. Аргумент - только словное значение, как сделать выбор слово/байт, пока не ясно и надо ли это, пока тоже не ясно.

Первый аргумент - текстовая строка, она должна быть обязательно заключена в двойные кавычки, и только в них. Внутри строки должен быть хотя бы один спецификатор формата, это конструкция заключённая в фигурных скобках, полную спецификацию смотрите здесь, но так как мы выводим число, то обрщайте внимание только на спецификацию, относящуюся к числам. Затем, через запятую, второй аргумент. И самое важное - тут отключена регистронезависимость, поэтому весь второй аргумент нужно записывать исключительно большими буквами.

Как это работает.

Парсер с помощью регулярного выражения выбирает строку - всё, найдёт между первой кавычкой в строке и последней, в строке, включая все кавычи между ними. Это считается объектом форматной строки.

Здесь не происходит автоматического перевода строки, это нужно делать самостоятельно. Допускаются символы \n, \r, \t, остальные экранированные символы не обрабатываются и выводятся как есть.

Затем, после строки в кавычках ищется один единственный аргумент через запятую. Если его нет, то выводится просто строка как есть, в том виде, как есть.

Если аргумент есть, то он обрабатывается как аргумент источник однооперандной команды, т.е. там допустимы все методы адресаций.

И получается, всё, что будет после второго аргумента - игнорируется, т.е. можно писать сколько хочешь аргументов, все они будут проигнорированы. И даже закрывающая скобка не обязательна, парность не проверяется.

#### Примеры

    $type ("Значение 'END_MAS': {0:06o}/{0:05d}/{0:#06x}\n", #END_MAS)

Тут мы выводим на экран значение метки/определения END_MAS в трёх системах счисления сразу.

    $type ("Значение по адресу CONTROL: {:06o}\n", CONTROL)

Тут мы выводим на экран содержимое по адресу метки CONTROL.

            mov #DATA, R1
            mov #10, R0
    1$:     $type ("{:} ", (R1)+)
            sob R0, 1$
            $type ("\n")

А вот тут мы выводим на экран 8 слов в десятичном виде (это вот для примера {:} - по умолчанию вывод числа в десятичнои виде) начиная с адреса DATA, а затем делаем перевод строки.

А это - общий пример использования скрипта в коде.

    ; тест
    Bgnc:           mov     #Bgnc, R1
                    clr     R0
                    mov     #Endc, R2
    1$:             add     (R1)+, R0
                    adc     R0
                    cmp     R1, R2
                    blo     1$
                    cmp     CSum, R0
                    beq     2$
                    mov     #errcs, R1
                    clr     R2
                    emt     20
                    halt
    2$:             mov     #okcs, R1
                    clr     R2
                    emt     20
                    halt
    errcs:          .asciz  "Ошибка контрольной суммы!"<12>
    okcs:           .asciz  "Контрольная сумма совпала!"<12>
                    .even
    Endc:
    CSum:           .word 0

    .SCRIPT         ; проверка отсечки комментариев
                    mov #Endc, R2
                    mov #Bgnc, R1
                    sub R1, R2
                    asr R2      ; размер в словах
                    clr R0
    1$:             add (R1)+, R0
                    adc R0
                    sob R2, 1$
                    mov R0, CSum
    .ENDS
                    .end

## Примеры работы с блоком расширенной арифметики.

Для организации работы с блоком расширенной арифметики необходимо перед его использованием в программе инициализировать его.

Это делается внесением в текст команды CALL ARIFM, после чего возможно написание программ с использованием мнемоники команд расширенной арифметики.

После трансляции такой программы её необходимо связать линкером с объектным модулем ARIFM.OBJ командой:

BKTurbo8 LI NAME prog.obj arifm.obj

полученная программа работает автономно. При поступлении кода команды расширенной арифметики происходит прерывание по вектору 10 и соответствующая команда обрабатывается, т.о. на данной машине возможно использования программного обеспечения, написанного для процессора 1801ВМ2.

На БК11М с КНГМД с прошивкой ПЗУ 326 вместо этого можно в начале программы сделать:

MOV #164040,@#10

MOV #340,@#12

DIV | - деление 32 разрядного слова Rn:Rn|1 на число. Регистр в команде должен быть чётным.
    MOV #75.,R1
    CLR R0
    DIV #10.,R0 ;Деление числа 75. на 10.,
    HALT ;в R0 - результат, в R1 - остаток.
MUL | - умножение регистра на число. Причём если номер регистра нечётный, то сохраняется только младшая часть результата. Rn:Rn|1 = Rn * src
    MOV #7,R1
    MUL #10.,R1 ;умножение 7*10=70 в регистре R1.
    HALT
    MOV #37252,R2
    MUL #1500.,R2 ;умножение 37252*1500. = 24063000.
    HALT ; R2 содержит ст. часть числа (557 == 367.)
     ; R3 содержит мл. часть числа (26030 == 11288.)
ASH | - арифметический сдвиг регистра вправо, влево на (-32 +32) позиции в зависимости от значения 5 бита аргумента сдвига. При 1 в 5 бите - сдвиг вправо, при нуле - влево.
    ASH #5,R1  ; сдвиг регистра R1 на 5 позиций влево.
ASHC | - арифметический сдвиг двойного слова, причём регистр с нечётным номером содержит младшую часть слова, а с чётным старшую, остальное аналогично команде ASH.
    MOV #0,R0
    MOV #^B1110011100011000,R1
    ASHC #7,R0 ; двигаем влево R0:R1 на 7 разрядов
    HALT ; R0 содержит ^b1110011
     ; R1 содержит ^b100011000
    MOV #^B1110011100011000,R1
    ASHC #-7,R1 ; вращаем вправо R1 на 7 разрядов
    HALT ; R1 содержит 0011000111001110
FMUL | - умножение чисел с плавающей запятой. B←A*B - результат на место аргумента B.
FDIV | - деление чисел с плавающей запятой. B←A/B - результат на место аргумента B. Если делитель ( B ) равен нулю, то результат в стек не записывается.
FADD | - сложение чисел с плавающей запятой, регистр указывает на адрес нахождения аргументов. B←A+B - результат помещается на место аргумента B.
FSUB | - вычитание чисел с плавающей запятой. B←A-B - результат в B.
             MOV   #MET,R5
            FSUB  R5    ;R5 указывает на адрес MET
            HALT
    MET:    .WORD A1,A2 ;два слова аргумента A
            .WORD B1,B2 ;два слова аргумента B

Формат чисел с плавающей запятой:

                  ┌──┬─────────────────┬─────────────┐
    ПЕРВОЕ СЛОВО :│15│14 . . . . . . 07│06 . . . . 00│
                  └──┴─────────────────┴─────────────┘
                    S      Порядок      ст. часть мантиссы

                  ┌──────────────────────────────────┐
    ВТОРОЕ СЛОВО :│15. . . . . . . . . . . . . . . 00│
                  └──────────────────────────────────┘
                        младшая часть мантиссы
                    S - знак числа.

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

## КОМПИЛЯЦИЯ

Компиляция исходного текста может производиться в двух форматах выдачи:

1. Трансляция с получением объектного модуля. (Режим CL)
2. Трансляция с получением загрузочного модуля. (Режим CO)

Объектный модуль позиционно независим и может быть затем связан с другими модулями или привязан сам по любому адресу, задаваемому ключом "-s" в командной строке.

Трансляция объектного модуля производится командой CL, с целью дальнейшей связки в виде загрузочного модуля командой LI, причём буквально с любого адреса, задаваемого ключом "-s" в командной строке.

Загрузочный модуль транслируется по адресу, установленному псевдокомандой .LA или задаваемому ключом "-s" в командной строке.

При компиляции в режиме CO опционально можно создать и сохранить скомпонованный объектный файл, в котором находится таблица имён меток этого модуля и возможно остаются ссылки на метки из внешних модулей, которые можно прилинковать к этому модулю позже, командой LI.

Также, такие объектники, скомпилированные командой CO потом можно скомпоновать в виде набора оверлейных модулей, собранных в один файл, которые работают в заданных страницах в заданных окнах БК-0011(М).

Это такая фича БК, ДВКшникам и УНКЦшникам не понять, а БКшникам приходится с этим жить.

### Пример команд для компиляции оверлейной программы.

Рассмотрим такой пример. У нас есть большая программа, которая имеет размер, допустим больше 32 Кб. Часть её размещается в странице 0, часть - в странице 1, которая подключается в окно 0, по адресу 40000, а оставшаяся часть - в странице 2, которая так же подключается в окно 0 по адресу 40000. Допустим мы так грамотно распределили подпрограммы, что странице 2 не нужны подпрограммы из страницы 1 и наоборот. Но все они вызываются из страницы 0.

Получается, что нам нужно собрать два оверлейных модуля, которые оба будут скомпонованы с адреса 40000: назовём их module1.ovl и module2.ovl.

Предположим, что module1 собирается из двух файлов file1_1.asm и file1_2.asm, а module2 - из одного file2.asm

Чтобы задать адрес компоновки, можно использовать псевдокоманду .LA <адрес> или ключ командной строки "-s<адрес>", у псевдокоманды приоритет, если задана она, то ключ командной строки не применяется.

Ключи и команды регистронезависимы, можно писать их как с большой, так и с маленькой буквы, как кому больше нравится.

Собираем оверлейный модуль для страницы 2, т.к. он состоит всего из одного файла, то тут всё просто. Вот такой командой:

-l -s040000 CO file2.asm

В результате будут созданы файлы: листинга file2.lst, и исполняемый файл file2.bin, который нужно переименовать в module2.ovl., можно обойтись без переименования, используя две команды вместо одной:

-o -l -s040000 CL file2.asm

-lmodule2 LI module2.ovl file2.obj

По команде CL объектный файл всегда генерируется позиционно независимым, но, если задан ключ "-s" в командной строке или если адрес компоновки задан псевдокомандой .LA, то адрес сохраняется в объектном файле как адрес компоновки по умолчанию, который в последствии можно изменить ключом "-s" в команде LI, либо, если объектный файл линкуется к другому объектному файлу, новый адрес компоновки вычисляется автоматически.

Теперь собираем оверлейный модуль для страницы 1, тут чуточку сложнее, т.к. он из двух файлов. Компилируем сперва второй файл:

-o -l CL file1_2.asm

В результате будут созданы файлы: листинга file1_2.lst и нужный нам объектный файл file1_2.obj.

Затем компилируем первый файл, это можно сделать так же командой CL, но поскольку мы и так знаем, что он у нас первый, и должен быть скомпонован с адреса 40000, то можем спокойно компилировать командой CO:

-o -l -s040000 CO file1_1.asm

Напомню, что, если мы в файле задали адрес компоновки псевдокомандой .LA, то можно не писать везде ключ командной строки "-s". В результате получаем файлы: листинга file1_1.lst, нужный нам объектный файл file1_1.obj и не нужный исполняемый файл file1_1.bin, который можно спокойно удалить.

Ну а теперь соберём оверлейный модуль для страницы 1:

-lmodule1 LI module1.ovl file1_1.obj file1_2.obj

В результате получаем файлы: листинга module1.lst, в котором не листинг, а таблицы меток, чтобы посмотреть, нет ли там неопределённых меток и нужный нам исполняемый файл module1.ovl.

Вот другая последовательность команд для сборки оверлейного модуля для страницы 1, используя команду CL:

-o -l CL file1_1.asm file1_2.asm

-l -s040000 LI module1.ovl file1_1.obj file1_2.obj

Результат получается аналогичный, но без ненужного промежуточного файла file1_1.bin.

Тут есть один тонкий момент, если мы одной командой компилируем сразу несколько файлов, то параметры ключей командной строки применяются ко всем ним. Именно поэтому тут нежелательно использовать ключ "-s". Поэтому адрес компоновки необходимо указать либо псевдокомандой .LA, либо при линковке.

Затем собираем модуль для страницы 0, аналогично вышеописанными способами. Но тут возникает проблема, как получить имена меток вызываемых подпрограмм из модулей 1 и 2? Есть довольно муторный способ - достать из листингов компиляций оверлеев таблицу меток, и вставить её в исходник для модуля страницы 0, это можно сделать раз, два, ну три. И хорошо, если оверлеи пересобирать больше не планируется. А иначе - очень быстро надоедает.

И вот тут появляется польза от ключа командной строки "-t" который по сути автоматизирует это действие.

Соберём оверлеи такими командами:

-o -l -s040000 CL file2.asm

-lmodule2 -tmodule2 LI module2.ovl file2.obj

-o -l CL file1_1.asm file1_2.asm

-lmodule1 -tmodule1 -s040000 LI module1.ovl file1_1.obj file1_2.obj

В результате, ко всему прочему будут созданы два дополнительных объектных модуля: module2_tbl.obj и module1_tbl.obj, которые содержат только таблицы значений меток, уже скомпонованных с адреса 40000 для модуля 2 и 1 соответственно.

Затем просто достаточно прилинковать эти объектники к компонуемой программе для страницы 0:

-o -l CL file0.asm

-l LI main.exe file0.obj module1_tbl.obj module2_tbl.obj

В результате получаем работоспособный исполняемый модуль для страницы 0, в который уже будут скомпонованы метки из оверлейных модулей. Постоянно и везде используемый ключ -l - для контроля, не забыли ли каких-нибудь меток.

#### Продолжение примера.

Давайте немного усложним задачу. Допустим, из оверлейного модуля 2 тоже нужно вызывать подпрограммы, находящиеся в главном модуле 0. Для того, чтобы собрать модуль 2, нам нужно будет прилинковать к нему таблицу меток модуля 0, а для того, чтобы собрать модуль 0, нужно прилинковать к нему таблицу меток модуля 2. Тут не обойтись без промежуточных операций.

Для начала, соберём объектный фай модуля 2 такой командой:

-l -o -s040000 -tmodule2 CO file2.asm

Получаем листинг, уже скомпонованный объектный файл file2.obj, и объектный файл меток module2_tbl.obj, который мы используем для сборки модуля 0. И ненужный исполняемый файл file2.bin, который пока всё равно неработоспособен.

Затем без всяких изменений собираем модуль 1:

-o -l CL file1_1.asm file1_2.asm

-lmodule1 -tmodule1 -s040000 LI module1.ovl file1_1.obj file1_2.obj

Затем собираем модуль 0, если он состоит всего из одного файла, то всё просто:

-o -l -tmodule0 CO file0.asm

Получаем всё то же, что и для модуля 2 - объектники и нерабочий пока бинарник.

Если модуль 0 состоит из нескольких файлов, то его собираем по сценарию модуля 1: сперва компилируем командой CL потом линкуем командой LI:

-o -l CL file0_1.asm file0_2.asm

-lmodule0 -tmodule0 -o LI module0 file0_1.obj file0_2.obj

Цель - получить уже скомпонованный объектный файл, и спец. таблицу меток.

И после этого, наконец мы можем уже собрать окончательные варианты рабочих бинарников.

-l LI module2.ovl module2.obj module0_tbl.obj

-l LI main.exe module0.obj module1_tbl.obj module2_tbl.obj

Вот в принципе и всё.

## ОШИБКИ ПРИ ТРАНСЛЯЦИИ

101 - Псевдокоманда .LA должна быть первой в тексте.

102 - Ошибка длины или направления перехода в команде SOB.

103 - Недопустимый символ в строке / Синтаксическая ошибка.

104 - Ошибка или отсутствие числового аргумента.

105 - Неправильная псевдокоманда.

106 - Неправильная ассемблерная инструкция.

107 - зарезервировано.

108 - зарезервировано.

109 - Ошибка длины перехода по оператору ветвления.

110 - Нет закрывающей '>' в числовом аргументе псевдокоманды .ASCII/.ASCIIZ.

111 - Ошибка в имени регистра.

112 - Ошибка в псевдокоманде.

113 - Метка уже определена ранее.

114 - Аргумент .BLKB слишком велик.

115 - Аргумент .BLKW слишком велик.

116 - Аргумент .ORG слишком велик.

117 - Нечётный адрес команды.

118 - Отсутствует .END

119 - Неопределённая метка в непосредственном выражении.

120 - Отсутствует переход у команды SOB.

121 - зарезервировано.

122 - зарезервировано.

123 - Ошибка или неверный метод адресации.

124 - Отсутствует второй операнд.

125 - Переполнение байтового аргумента.

126 - Переполнение словного аргумента.

127 - Неожиданный конец строкового аргумента.

128 - Ошибка в числе с плавающей точкой.

129 - Невозможно открыть файл include.

130 - Невозможно открыть файл insert.

131 - Ошибка в числовом аргументе.

132 - Ошибка в имени регистра FPU.

133 - .ENDS без .SCRIPT.

134 - Неожиданный конец строки.

135 - Вставляемый элемент слишком велик.

136 - Аргумент .ALIGN слишком велик.

137 - Отсутствует закрывающая скобка.

138 - В выражении нельзя использовать скобки.

139 - Ошибка разбора арифметического выражения.

Так же существуют ещё разные диагностические сообщения об ошибках, не связанные с трансляцией текста, их список не приводится.

При трансляции текста выдаётся номер ошибки, номер строки и при возможности сама строка, в которой возникла ошибка.

# Конвертер бинарных объектов в объектные модули кросс ассемблера Turbo8.

Ещё есть утилита BKbin2obj, которая генерирует из бинарных объектов специальные объектные модули, которые потом можно слинковать с объектниками из исходников. Это такой аналог включения ресурсов в программу.

Эта утилита может конвертировать файлы изображений в БКшные изображения. Алгоритмы преобразования взяты из проекта pdp11asm vinxru, один к одному, я не разбирался, как они работают. Потому что не было необходимости.

Но, на мой взгляд, это какие-то специфичные алгоритмы для конкретных специфичных случаев.

Тут надо конкретики добавить. А ещё разобраться с алгоритмами работы с картинками.

Автор (не я, а автор оригинального БКшного Turbo8) приносит свою благодарность:

А. Надежину: | за ANDOS, TURBO4 и ещё много прекрасных программ.
А. Надежину: | за терпение, с которым он давал советы начинающему программисту-любителю.
А. Надежину: | за подарок - TURBO6M.
С. Камневу: | за великолепную сервисную оболочку DiskMASTER и ряд полезных советов.
В. Ретуновскому & А. Суханову: | за TRACER.
С. Клименкову: | за PARADISE.
М. Королеву: | за DESS, READER14 и MKDOS, с которой и начал автор работу с БК.
Д. Бутырскому: | за TURBO7MK, который хоть и не был в числе предшественников, но из которого была заимствована одна идея.
Д. Романову: | за систему VorteX, в которой было написано данное описание, и с которой вообще автор провёл много приятных минут.
П. Суходольскому: | за АОН вообще и РУСЬ14 в частности.
В. Тукову, | благодаря которому автор познакомился с БК.
И, конечно, В. Коренкову, "отцу-основателю" серии ассемблеров TURBO, и авторам TURBO4, TURBO5, TURBO6, без которых этой работы не было бы вообще.
Хотелось бы также поблагодарить своего кота Василия за моральную поддержку (не очень мешал).
Автор ассемблера Turbo8DK
Крылов Дмитрий Константинович 398-83-12 Москва, сентябрь 1995 г.

Ну а я по традиции присоединяюсь к благодарностям и в свою очередь благодарю Крылова Дмитрия Константиновича за ассемблер Turbo8DK, которым я только и пользовался, после появления дисковода у моей БКшки, и так привык к нему, что вот написал кросс ассемблер на его основе.

© gid 2012-2025