Искра-226/Кодирование BASIC-файлов

Материал из Emuverse
Версия от 18:30, 26 августа 2026; Panther (обсуждение | вклад) (Новая страница: «{{emuverse}} '''Оттранслированное представление BASIC 02''' — внутренний формат записи программы Искра-226, включаемый параметром <code>T</code> оператора <code>SAVE DC</code>. Имена операторов и функций заменены однобайтовыми токенами, имена переменных — индексами; текст в...»)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Этот документ создан для Emuverse и распространяется на условиях лицензии CC-BY-SA-3.0.

Оттранслированное представление BASIC 02 — внутренний формат записи программы Искра-226, включаемый параметром T оператора SAVE DC. Имена операторов и функций заменены однобайтовыми токенами, имена переменных — индексами; текст в потоке остаётся только внутри строковых литералов.

Внешняя структура файла, указатель каталога и заголовочный сектор описаны в статье Файловая система; оттранслированное представление отмечено битом 0 байта 9 заголовочного сектора. Смысл дисковых операторов — в статье Файлы данных BASIC 02.

Сборка потока

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

Пролог

Смещение Размер Значение
0 2 L1 — длина таблицы 1 в байтах, запись 8 байт
2 2 L2 — длина таблицы 2 в байтах, запись 4 байта
4 2 L3 — длина таблицы 3 в байтах, запись 4 байта
6 L1 таблица 1
6+L1 L2 таблица 2
6+L1+L2 L3 таблица 3

Длины — 16-битные, старший байт первым. Число записей: L1/8, L2/4, L3/4.

Программа начинается по смещению 6 + L1 + L2 + L3.

Четырёхбайтовое поле

У части файлов между таблицами и первой записью строки лежат четыре лишних байта: 0A 19 и двоично-десятичное число, своё у каждого файла. Назначение не установлено.

Наличие поля определяется проверкой записи по вычисленному смещению: оба байта номера строки — правильное двоично-десятичное число, байт длины не меньше единицы, байт по адресу p + 2 + len равен FE. Если проверка не проходит, а по смещению +4 проходит — поле присутствует.

Записи строк

<номер строки: 2 байта BCD> <len: 1 байт> <тело>

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

len считается включая сам байт len; следующий разделитель стоит по адресу адрес(len) + len.

Записи пересекают границу сектора; служебные байты в длину не входят.

Выравнивание

Если запись не помещается в остаток сектора, остаток заполняется нулями. Правило: после разделителя FE по смещению q

b = (⌊q / 254⌋ + 1) × 254
если b > q и все байты потока с q по b−1 нулевые, то q = b

Переход к следующему сектору выполняется только тогда, когда весь остаток текущего 254-байтового куска нулевой. Длина выравнивания доходит до шести байт.

Строка с номером 0 кодируется байтами 00 00 <len>; безусловный пропуск пары нулей перед номером строки разрушает разбор таких файлов.

Операторы

<токен глагола: 1 байт> <len: 1 байт> <операнды: len байт>

len — длина только операндов; len = 00 означает оператор без операндов. Явная длина позволяет разрезать программу на операторы, не зная значений токенов, и служит проверкой синхронизации.

Пример разбора

Строка 2000 файла #СТАТИСТ, длина 0x54 = 84:

20 00 54
23 01 23                          GOSUB ' 35
4C 39 E3 1F «БУДЕТЕ …» DD 00 DD E3 12 «…» DD
                                  PRINT "…";F¤;"…";
41 01 5C                          INPUT F%
36 03 5D D9 0E                    N7=N3
24 06 0E D7 11 D3 20 01           IF N3<N6THEN2001
36 03 5D D9 11                    N7=N6

Таблица глаголов

Значения назначены тематическими группами. Составные дисковые команды получают один токен на всю фразу.

Токен Оператор Токен Оператор
1E IF END THEN 52 NEXT
21 GOTO 53 REWIND
22 GOSUB 54 SELECT
23 GOSUB' 55 BACKSPACE
24 IF 56 REM
25 KEYIN 57 FOR
26 ON 58 SKIP
27 DEFFN' 59 END
28 PRINTUSING 5A DEFFN
29 DATA 5B DATA RESAVE
2A SAVE 5C RES
2B RENUMBER 5D UNPACK(
2C CLEAR 5E RETURN
2D LOAD 5F TRACE
2E LIST 61 OR(
2F RUN 62 XOR(
30 RETURN CLEAR 63 ADD C
33 LIST S 64 INIT
34 ON ERROR 65 LIST RES
35 LET 66 DATA LOAD BT
36 присваивание без ключевого слова 68 DATA SAVE BT
37 COM CLEAR 6D COPY
38 TRACE OFF 6E DATA SAVE BA
39 DATA LOAD 6F DATA SAVE DA
3A DEFFN' с текстом клавиши 70 DATA LOAD BA
3F % — краткая форма REM 71 DATA LOAD DA
40 $GIO 72 LOAD DA
41 INPUT 73 SAVE DA
42 STOP 74 DATA LOAD DC
43 AND( 75 DATA LOAD DC OPEN
44 READ 76 DATA SAVE DC
45 BOOL 77 DATA SAVE DC CLOSE
46 DIM 78 DATA SAVE DC OPEN
47 CONVERT 79 DBACKSPACE
48 PACK( 7A DSKIP
49 DATA SAVE 7B LIMITS
4A ADD 7C LIST DC
4B BIN( 7D LOAD DC
4C PRINT 7E MOVE
4D ROTATE 7F MOVE END
4E COM 80 SAVE DC
50 HEXPRINT 81 SCRATCH
51 RESTORE 82 SCRATCH DISK
83 VERIFY

Таблица прочитана из интерпретатора на загрузочном диске: с адреса 0x1840 лежит алфавитный список 51 однословного ключевого слова без разделителей, с 0x192E — 33 записи сочетаний вида «индекс первого слова плюс 0x60, текст продолжения», с 0x19B6 — 84-байтовый массив перестановки, дающий каждой записи её токен.

DEFFN' в таблице один, с токеном 3A; токен 27 (форма с формальными параметрами) машина ставит сама при разборе.

RES (5C) — самостоятельное ключевое слово; RESTORE собирается из RES и продолжения TORE. Что делают RES и LIST RES, документация не описывает.

Двухбайтовые глаголы

Матричные, символьные и графические операторы кодируются префиксом 06 и подкодом; байт длины стоит после подкода:

06 <подкод> <len> <операнды>
Подкод Оператор Подкод Оператор
00 PLOT 15 DRAW
01 MAT присваивание 19 NPLOT
02 MAT REDIM 1A ¤MOVE
03 MAT READ 1B TURN
04 MAT INPUT 1C STRETCH
05 MAT PRINT 1D FRAME
06 MAT COPY 1E LABEL
09 MAT MOVE (?) 1F ¤COPY
0A MAT SEARCH 22 ¤LET
0B MAT SORT (?) 23 WINDOW
0C ¤TRAN( 24 LINPUT
0D ¤PACK (?) 25 ASMB
0E ¤UNPACK (?) 26 REPLACE
0F ¤OPEN
13 DOT
14 DDRAW

Имена ORIGIN, DNPLOT и NDRAW встречаются в текстовых листингах, но подкодов не имеют: все подкоды, встречающиеся в оттранслированных файлах, уже опознаны.

Действие графических операторов и формат самого рисунка описаны в статье Графический буфер BASIC 02.

Таблица операндов

Токены операндов прочитаны из интерпретатора с адреса 0x1A0C: пул слов и таблица 16-битных указателей на них с адреса 0x1B18, младший байт первым, 114 слов. Токен слова — его номер в пуле плюс смещение прогона; прогонов четыре, по числу позиций разбора.

Двузначные токены

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

Токен В позиции операции В позиции операнда
D2 STEP параметр T оператора SAVE DC
D5 <> AT(; ' в $GIO
D6 <= BEG; ¤ после DATA SAVE DC и SAVE DC
D7 < END
D8 >= ROUND(
D9 = OPEN
DB # — номер строки таблицы устройств; «2» — второй аргумент VAL( и BIN(
DC / — деление / — префикс адреса устройства
DE , однобайтовый литерал: значение в следующем байте
DF * TAB(
E0 ^ ссылка на массив целиком
E5 XOR дробная константа
E6 OR константа с порядком
E7 AND константа, 2 байта BCD
E8 ARC константа, 1 байт BCD
E9 - бинарный - унарный
EC TRN POS(
ED INV LEN(
EE IDN NUM(
EF ZER VAL(
F0 CON FN<имя>(

Ключевые слова TRN, INV, IDN, ZER и CON встречаются только внутри оператора MAT. Слова OPEN и ARC взяты из таблицы интерпретатора; в оттранслированных файлах D9 встречается только как знак равенства, а E8 — только как константа.

Однозначные токены

Токен Значение
00C9 ссылка на переменную по индексу
CA FROM
CB ALL
CC GOSUB в составе ON … GOSUB
CD GOTO в составе ON … GOTO
CE, CF буквы B и F
D0 )
D1 TO
D3 THEN, далее 2 байта BCD — номер строки
D4 >
D9 =
DD ;
E1 STR(
E2 HEX(: E2 <len> <len байт>
E3 строковый литерал в кавычках: E3 <len> <len байт КОИ-8>
E4 строковый литерал в апострофах, кодирование то же
E8 константа, 1 байт BCD
EA +
EB ( — группирующая скобка
F1 #PI
F2F8 ABS(, INT(, RND(, SGN(, SQR(, LOG(, EXP(
F9FE SIN(, COS(, TAN(, ARCSIN(, ARCCOS(, ARCTAN(
FE вне выражения — разделитель записей строк

Ссылками на переменные являются значения 00C9, а не 00BF: первый токен — CA. Индексы C0C9 встречаются в программах более чем со 192 переменными.

Два вида строковых литералов

Строки в кавычках система выводит как строки из прописных букв, строки в апострофах — из строчных.[1] Так на машине без строчных букв на клавиатуре задаются строчные символьные константы.

Токен Форма записи Регистр
E3 "…" прописные
E4 '…' строчные

Числовые константы

Токен Формат
DE сырой байт, не BCD — байтовые константы и адреса устройств
E5 описатель + BCD
E6 то же + байт порядка
E7 2 байта BCD, 0–7999
E8 1 байт BCD, 0–99

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

Запись Байты Описатель
.5 E5 01 50 0 / 1
2.5 E5 12 25 1 / 2
.196854 E5 06 19 68 54 0 / 6
51840 E5 55 51 84 00 5 / 5
2.718281828 E5 1A 27 18 28 18 28 1 / 10
1E6 E6 11 10 06 1 / 1, порядок 6

Токен E5 служит и для целых чисел больше 9999 — всего, что не помещается в E7; дробной части при этом нет, обе тетрады описателя равны.

Длина хвоста — 1 + ⌈младшая тетрада / 2⌉ байт для E5 и на один байт больше для E6.

Верхняя граница E7 — 7999: восемь тысяч кодируются общей формой E5 44 80 00.

Запись с порядком по значению не восстанавливается: 1E6 и 1000000 — одно число, но разные байты.

Номер метки в GOSUB' и первый байт операндов DEFFN' хранятся двоичными, не в BCD. То же относится к номерам строк, которые записываются сырыми парами BCD без токена: диапазоны в SAVE, LOAD, SAVE DC, LOAD DC, RUN, номер строки у RESTORE, IF END THEN, KEYIN и LIST S.

Таблицы переменных

Таблица 1

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

Запись — 8 байт:

Смещение Размер Значение
0–1 2 адрес переменной в памяти, младший байт первым
2–3 2 тип: 00 08 — символьная, 2D 08 — числовая или целая; иное значение — вторая размерность
4–5 2 число элементов (первая размерность), младший байт первым
6–7 2 размерный код, младший байт первым

Размерный код равен 2 × размер элемента, младший бит означает, что длина задана явно:

  • целая — 4 (2 байта);
  • числовая — 16 (8 байт);
  • символьная длины L2L + 1;
  • символьная с длиной по умолчанию (16) — 32, код чётный.

Нечётность кода не является признаком символьного типа; тип берётся из байтов 2–3 либо из флага таблиц 2 и 3.

Двумерные массивы. Вторая размерность записывается в байты 2–3, на место признака типа; первая остаётся в поле числа элементов. Размерности не перемножаются, DIM восстанавливается точно. Практическое правило: старший байт поля 2–3 равен 08 — массив одномерный, иначе поле занято второй размерностью.

Исключение — неявно объявленные массивы: у переменной, к которой обратились с индексом без DIM, число элементов равно 10, а в байтах 2–3 стоят значения 01 27 или 07 59, размерностями не являющиеся. Такие массивы следует выводить как одномерные (10).

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

Размер выделяемой памяти:

  • скалярная символьная переменная — объявленная длина, округлённая вверх до чётной;
  • массив — N × размер элемента + 6, где 6 байт занимает дескриптор.

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

Таблицы 2 и 3

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

Смещение Размер Значение
0–1 2 адрес переменной, младший байт первым; 0000, если память не выделена
2 1 флаг типа
3 1 назначение не установлено

Записи идут в порядке убывания индекса переменной. Если

N = L2 / 4 + L3 / 4

то переменная с индексом i описана записью на позиции N − 1 − i, считая позиции подряд: сначала таблица 2, затем таблица 3. Отсюда N — общее число переменных программы.

Таблица 3 приходится на самые младшие индексы — те, что занимает оператор COM.

Флаг типа:

Бит Значение
0 (0x01) у переменной есть дескриптор в таблице 1
3 назначение не установлено
4 (0x10) числовая действительная
5 (0x20) символьная
7 назначение не установлено
ни 4, ни 5 целая (суффикс %)

Связь с таблицей 1. Число записей с установленным битом 0 равно L1 / 8, и соответствие порядковое:

k = 0
для pos = 0 … N−1:
    если флаг[pos] и 1:
        дескриптор переменной (N − 1 − pos) = запись таблицы 1 номер k
        k = k + 1

Это даёт точное соответствие «индекс переменной → запись таблицы 1» без разбора операторов DIM и COM. Сопоставление по порядку объявления ошибочно: COM объявляет и обычные скаляры, у которых дескриптора нет, а после MAT REDIM в операнде DIM стоят «теневые» индексы.

У переменных с дескриптором таблицы 1 поле 0–1 содержит не адрес, а небольшое число, растущее примерно на восемь с каждой такой переменной. Назначение не установлено.

Состояние на момент сохранения

Оттранслированный файл — снимок памяти после запуска, а не перевод текста.

Массив, выросший при выполнении оператора MAT REDIM за пределы исходного выделения, получает новый дескриптор в таблице 1 и новый индекс переменной, причём интерпретатор правит операнд в самом операторе DIM, оставляя код и MAT REDIM работать со старым индексом. Если новый размер не превышает объявленного, память переиспользуется и запись остаётся одна.

Поэтому операторы DIM как источник соответствия «индекс — дескриптор» непригодны.

Индексы и имена переменных

Индексы назначаются в порядке первого появления переменной в тексте программы. Операторы COM и DIM делят единое пространство индексов; COM выполняется первым и получает индексы с нуля.

Ключ переменной — имя вместе с признаком массива: A, A%, , A() и A%() — разные переменные с разными индексами.

Имена переменных в оттранслированном файле не сохраняются. Таблицы содержат только дескрипторы памяти. Читаемый листинг из такого файла восстановить нельзя: восстанавливается структура программы, тип каждой переменной, «массив или скаляр» и размерности, но не имена.

Операнды

Дисковые операторы

Первый байт операндов — код устройства или режима, а не ссылка на переменную:

Байт Значение
00 F — первый дисковод
01 R — второй дисковод
02 T — обращение по номеру строки таблицы устройств (#n)
79 01 D6                                   DBACKSPACE BEG
7A 01 D7                                   DSKIP END
76 01 D7                                   DATA SAVE DC END
79 04 DB E8 01 DE D6                       DBACKSPACE #1,BEG
76 07 D6 DB E8 02 DE E0 12                 DATA SAVE DC ¤#2,W()
82 0B 01 06 D9 E8 05 DE D7 D9 E7 10 00     SCRATCH DISK RLS=5,END=1000

В последнем примере 06 — ключевое слово LS, D9 — знак равенства, D7END. Байт 05 после законченного выражения у DSKIP и DBACKSPACE — параметр S («в секторах»).

Формы SAVE DC, LOAD DC, LIST DC:

80  <приставка> [D2 = T] [EB <старое имя либо резерв> D0] <имя> [<нач> [DE <кон>]]
7D  <приставка> <имя> [<нач> [DE <кон> [DE <куда продолжать>]]]
7C  <приставка> [<имя>]

Списки

Список принимающих переменных пишется без разделителей:

41 02 0A 0B                        INPUT T0,N0
74 03 05 0C 0D                     DATA LOAD DC A1¤,E1,E2
46 04 00 01 02 03                  DIM X(300),Y(300),N(2),G¤(300)5

Список выражений разделяется DE:

76 06 D6 05 DE 0A DE 0B            DATA SAVE DC ¤A1¤,T0,N0

Пропущенный параметр не кодируется ничем — между разделителями просто нет операнда:

06 1E 09 E0 12 DE DE DE 11 E8 01 D0    LABEL B6¤(),,,B3¤(1)

Индексация массивов

У обращения к элементу массива нет открывающей скобки: ссылка на переменную сама открывает список индексов, а D0 его закрывает. Токен EB используется только как группирующая скобка в выражениях и для размерностей в MAT REDIM.

01 32 D0                → 01(32)
EB 34 E9 33 E9 E8 01 D0 → (34*33*1)

Ссылка на массив целиком — E0 <индекс>, законченное значение: после неё разбор ожидает операцию или следующий элемент списка. В операторах, операнды которых — список значений, возведения в степень быть не может, поэтому E0 там всегда ссылка на массив.

Скобки

Конструкция Открывающая Закрывающая
индекс массива нет D0
STR(, ABS(, SQR( и прочие явные функции в токене D0
POS(, LEN(, NUM(, VAL( в токене нет
AT( в токене нет
TAB(, ROUND( в токене D0
группировка в выражении EB D0

У глаголов, имя которых оканчивается на (, закрывающей скобки в потоке нет:

Глагол Где находится закрывающая скобка
43 AND(, 61 OR(, 62 XOR( в конце операторной части
48 PACK(, 5D UNPACK(, 06 0C ¤TRAN( сразу за литералом формата
4B BIN( после приёмника, вместе со знаком =

Неявные функции считаются по уровням, а не одним счётчиком. Закрывающая скобка вложенной явной конструкции неявную функцию не закрывает:

EC  E1  E0 2A  E8 01  D0  D7  DE 20  D5  E8 00  D3 02 70
POS(STR( V¤()  , 1    )   <   20     )   <>  0   THEN 270

Здесь D0 закрывает только STR(, а POS( заканчивается на D5.

Неявная первая запятая

Функция STR( первую запятую не кодирует: STR(Z¤,67)E1 10 E8 67 D0. Вторая и последующие кодируются DE. У прочих операторов неявной первой запятой нет.

Первый аргумент STR( индексируется только по признаку массива из таблиц переменных: заглядывание вперёд читает STR(Z¤,67) как Z¤(67). То же относится к приёмнику BIN(, приёмникам INIT и KEYIN и имени файла у LIMITS — за ними сразу идёт следующий операнд, без разделителя.

Что не кодируется

Оператор В потоке В листинге
INIT 64 04 E3 01 2D 15 INIT ("-")U¤ — значение в скобках, запятой нет
CONVERT 47 0F 48 D1 E1 … D0 E3 02 «##» CONVERT P% TO STR(…),(##)
$GIO с адресом 40 0D DC DE 34 DE E2 06 … 22 ¤GIO /34,(HEX(…),A¤)
VERIFY 83 … 02 DB 39 E8 01 D0 DE 16 … 17 … VERIFY T#D%(1),(X(1),Y(1))
¤TRAN( 06 0C 06 0D DE 12 D0 DE 00 ¤TRAN(Q¤,L0¤)R — запятая после ) не печатается
LABEL 06 1E 0A E0 1F E8 03 DE DE DE E3 01 51 LABEL B¤()3,,,"Q" — размер вплотную к имени
MAT 06 01 04 E0 54 D9 EF MAT C%=ZER — имя массива без ()
RESTORE 51 05 E8 01 DE 48 50 RESTORE 1,4850
SAVE, LOAD 2A 0A DD 10 52 15 DE 52 15 DE 52 25 SAVE Z¤5215,5215,5225
LINPUT 06 24 2C E3 28 … E9 32 LINPUT "…",-L¤ — запятая перед унарным минусом
INPUT с приглашением 41 14 E3 11 «…» 00 INPUT "…",K — запятая после литерала

Оператор SELECT

Первый байт операндов — код группы устройств, записи разделяются DE. Ширина строки после EB — двоичное 16-битное число, старший байт первым.

Код Группа Пример
00 #n — строка таблицы устройств 00 01 18 = #118; 00 01 18 00 = #118F
01 D — градусы операндов нет
02 R — радианы операндов нет
03 G — грады операндов нет
05 P — пауза после строки 05 01 = SELECT P1
06 LIST 06 0C
07 PRINT 07 0C EB 00 82 = SELECT PRINT0C(130)
08 PLOT 08 10 DE …
09 TAPE 09 1B EB 1F 3F
0A DISK 0A 18 01 = SELECT DISK18R
0B CI
0C CO 0C 05 EB 00 50

В записях группы 00 дисковод указывается не всегда: SELECT #118 даёт трёхбайтовую запись, SELECT #118F — четырёхбайтовую.

Параметры оператора CLEAR кодируются той же таблицей: 11V, 12N, 14P.

Цепочка операторов DATA

Два последних байта операндов каждого оператора DATA — указатель на следующий оператор DATA, младший байт первым; у последнего в программе он равен 00 00. По этой цепочке работают READ и RESTORE. Значения — адреса в памяти интерпретатора, а не смещения в файле, и из текста программы не восстанавливаются.

Эти два байта нельзя разбирать как токены.

Тело оператора — список значений без разделителей:

29 1E E8 31 E7 03 34 E8 00 E8 28 E7 03 06 E8 31 … C9 04
      31    334      0     28    306      31

Поэтому внутри DATA токены E5, E6 и E7 всегда читаются как константы, а не как XOR/OR/AND. То же в MAT REDIM, где длина элемента идёт сразу за закрывающей скобкой:

06 02 0A E0 04 EB E7 01 26 D0 E7 01 28    MAT REDIM A¤(126)128

Подпрограммы с параметрами

Глагол Операнды
23 GOSUB' <метка: 1 байт двоичный>, затем фактические параметры — выражения через DE
27 DEFFN' <метка> <4 байта адреса входа>, затем формальные параметры — индексы подряд, без разделителей
3A DEFFN' <метка> <4 байта адреса> <один литерал> — текст клавиши специальных функций
5A DEFFN <имя> <2 байта рабочего поля> <формальная переменная> <выражение>

Скобки списка параметров не кодируются.

23 0B 05 E8 01 DE E8 01 DE 0D E9 E8 02    GOSUB ' 5(1,1,N-2)
27 08 05 00 00 00 00 12 20 21             DEFFN ' 5(N1,D1,D2)
3A 08 1F 00 00 A4 03 E2 01 0D             DEFFN ' 31 HEX(0D)

Четыре байта после метки у 27 и 3A — адрес входа, который интерпретатор заполняет при первом обращении; нулевыми они бывают только там, где подпрограмму ни разу не звали. При разборе их следует пропускать.

Токен 3A подпрограммы не задаёт: это текст, подставляемый при нажатии клавиши, и GOSUB' на него не переходит.

Функции пользователя

5A <имя> <lo> <hi> <формальная переменная> <выражение>
F0 <имя> <выражение> D0

Имя функции — латинская буква либо цифра, записанная сырым кодом символа. Это не индекс переменной: пространства имён разные, одна и та же буква может быть и переменной, и именем функции. Знака равенства в потоке нет, тело идёт сразу за байтом формальной переменной. Два байта между именем и формальной переменной — рабочее поле; у последнего определения в программе там нули.

5A 48 00 00 2A EB 2A EA F2 2A D0 D0 DC E8 02    DEFFN H(X)=(X+ABS(X))/2

В операторах AND(, OR(, XOR( байт F0 в конце операндов — не функция, а шестнадцатеричная маска.

Разбор выражений

Два состояния: «ожидается операнд» и «ожидается операция».

  • Сброс в «операнд»: начало операндов оператора, после DE, DD, D9, D1, знаков сравнения, после EB и функций, после любой бинарной операции.
  • Переход в «операцию»: после ссылки на переменную, константы, литерала, D0.
  • Унарный минус (E9 в позиции операнда) состояние не меняет.

Заглядывать вперёд можно только в том состоянии, в каком лексема потом будет прочитана: DE в позиции операции — запятая, в позиции операнда — однобайтовый литерал, съедающий следующий байт.

Разрешение «массив или скаляр» после ссылки на переменную:

Следующий токен Вывод
однозначный операнд (E8, E7, E3, EB, ссылка на переменную) список индексов
однозначная операция (DC, EA, D9, D7 и прочие) продолжение выражения
двузначный токен операция

Левая часть присваивания: цели идут вплотную, без разделителей, до первого D9 на нулевой глубине.

36 07 0F 10 11 12 D9 E8 00        S1,S2,S3,M=0

Связки условий XOR, OR и AND равноправны и вычисляются слева направо;[1] старшинства между ними нет.

В операторе MAT SEARCH знак сравнения стоит в позиции операнда — сразу после запятой, отделяющей искомое значение:

06 0A 08 E0 20 DE D4 22 D1 E0 1E    MAT SEARCH L4¤(),>A¤TON4¤()

Алгоритм разбора

  1. Прочитать запись каталога и заголовочный сектор; убедиться, что бит 0 байта 9 установлен.
  2. Собрать поток, беря с каждого сектора ровно 254 байта без обрезки хвостовых нулей.
  3. Прочитать L1, L2, L3; перейти к смещению 6 + L1 + L2 + L3; проверить наличие четырёхбайтового поля.
  4. Построить таблицу переменных: N = L2/4 + L3/4, индекс на позиции pos равен N − 1 − pos, тип из флага; записи с битом 0 по порядку сопоставить записям таблицы 1.
  5. Для каждой записи строки: номер из двух байтов BCD, длина из третьего, тело до следующего FE. Если по отсчитанной длине FE нет — поток рассинхронизирован, разбор остановить.
  6. После FE: если весь остаток текущего 254-байтового куска нулевой, перейти к началу следующего.
  7. Тело разрезать на операторы <глагол><len><операнды>, учитывая двухбайтовые глаголы с префиксом 06.
  8. Перед разбором операндов учесть особые формы: у DATA два последних байта — указатель цепочки, у дисковых операторов первый байт — код F/R/T, у GOSUB' и DEFFN' — двоичная метка, у SELECT и SCRATCH DISK — собственный формат.
  9. Операнды — автомат состояний со стеком уровней скобок.
  10. Контроль: израсходовано ровно len; стек пуст к концу оператора; число записей таблиц 2 и 3 с битом 0 равно L1 / 8.

Обработку файла прекращать на первом секторе с маркером 1C.

Не установлено

  1. Подкоды операторов ORIGIN, DNPLOT, NDRAW.
  2. Разметка аргумента в скобках у MAT TRN и MAT INV; кодирование матричной арифметики (+, -, *, (K)*).
  3. Кодирование отрицательного порядка числовой константы.
  4. Маска hh у ¤TRAN(; признаки *, %, # у MAT SEARCH; формы LIST V, LIST *, LIST %, LIST ¤; форма INPUT ¤<переменная>.
  5. Грамматика операндов у глаголов 38, 39, 49, 5B, 65, 7F: байты известны, вхождений нет.
  6. Смысл операторов RES (5C) и LIST RES (65).
  7. Назначение четырёхбайтового поля 0A 19 <BCD> перед программой.
  8. Байт 3 записей таблиц 2 и 3, биты 7 и 3 флага, поле 0–1 у переменных с дескриптором таблицы 1.
  9. Различение «строка-скаляр или массив строк» при нулевых адресах в таблице 1.

Примечания

  1. 1,0 1,1 Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987. — 264 с.: ил.

Источники

  • Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987.
  • Таблицы ключевых слов и операндов — из образа интерпретатора на загрузочных дисках, адреса 0x1840 и 0x1A0C.
  • Реконструкция по 94 оттранслированным файлам (15 735 строк) и 28 текстовым листингам, в том числе 12 парам «текст + токены».