Искра-226/Файловая система: различия между версиями

Материал из Emuverse
(new info)
 
(не показаны 3 промежуточные версии этого же участника)
Строка 25: Строка 25:


Размер файла на диске уменьшать нельзя, поэтому при перезаписи файла более коротким содержимым '''старые данные в хвосте не затираются'''. Там могут остаться читаемые фрагменты предыдущих версий файла, иногда в другом представлении. Их нельзя принимать за часть текущего файла, поэтому необходимо завершать обработку файла при нахождении первой control record.
Размер файла на диске уменьшать нельзя, поэтому при перезаписи файла более коротким содержимым '''старые данные в хвосте не затираются'''. Там могут остаться читаемые фрагменты предыдущих версий файла, иногда в другом представлении. Их нельзя принимать за часть текущего файла, поэтому необходимо завершать обработку файла при нахождении первой control record.
Искра-226 поддерживает работу с двумя типами файлов: ПФ — «Программные файлы» и ФД — «Файлы данных». Программные файлы всегда содержат программу на Бейсике, в текстовом или упакованном токенизированном виде. Файлы данных, соответственно, данные, доступные из программ пользователя.
В соответствии с документацией, Искра-226 поддерживает прямой доступ программ к секторам диска, при этом к записанным таким образом дискам всё нижеизложенное может быть неприменимо, так как форматом хранения полностью управляет программа пользователя по своему усмотрению.
Также, в наличии имеется несколько системных дисков, на которых содержатся различные версии интерпретатора Бейсика, с которых производится начальная загрузка компьютера. Эти диски имеют свой формат и здесь не рассматриваются.


== Указатель каталога ==
== Указатель каталога ==
Строка 72: Строка 78:


=== Пример ===
=== Пример ===
 
  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  10 80 00 31 00 3F 00 00 44 49 47 20 44 45 4D 20
  10 80 00 31 00 3F 00 00 44 49 47 20 44 45 4D 20
 
└───┘ └───┘ └───┘      └─────────────────────┘
Активная запись, программа, секторы 49…63, имя <code>DIG DEM</code>.
Активная запись, программа, секторы 49…63, имя <code>DIG DEM</code>.


Строка 225: Строка 231:
'''Программа начинается по смещению <code>6 + L1 + L2 + L3</code>.'''
'''Программа начинается по смещению <code>6 + L1 + L2 + L3</code>.'''


Проверено на трёх файлах:
Проверено на четырёх файлах:


{| class="wikitable"
{| class="wikitable"
Строка 235: Строка 241:
|-
|-
| <code>ДЕМ6</code> || 392 || 256 || 0 || 654 || <code>10 REM "ДЕМ6". ГОРОСКОП</code>
| <code>ДЕМ6</code> || 392 || 256 || 0 || 654 || <code>10 REM "ДЕМ6". ГОРОСКОП</code>
|-
| <code>EDITOR</code> || 448 || 800 || 0 || 1254 || <code>10 % EDITOR.  ВЕРСИЯ 4.1</code>
|}
|}


Строка 241: Строка 249:
==== Таблица 1 ====
==== Таблица 1 ====


Записи по 8 байт. Байты 0–1 — адрес, младший первым, строго возрастающий от записи к записи. Шаг между соседними адресами принимает значения 2, 4, 8, 12, 48, что соответствует размерам объектов: 2 байта целочисленная переменная BASIC 02, 8 байт — число в BCD, кратные — массивы. Назначение байтов 2–7 не установлено.
'''Таблица 1 описывает переменные, объявленные в <code>DIM</code>.''' Запись — 8 байт:


'''Таблица 1 описывает переменные, объявленные в <code>DIM</code>,''' и номер записи в ней равен индексу переменной в коде. В файле <code>ДЕМ6</code> три оператора <code>DIM</code> перечисляют индексы <code>00</code><code>05</code>, <code>06</code><code>29</code> и <code>2A</code><code>30</code>, то есть 49 переменных подряд; L1 = 392 = 49 × 8. Совпадение точное.
{| class="wikitable"
! Смещение !! Размер !! Значение
|-
| 0–1 || 2 || адрес переменной в памяти, младший байт первым
|-
| 2–3 || 2 || тип: <code>00 08</code> — символьная, <code>2D 08</code> — числовая или целая
|-
| 4–5 || 2 || для массивов — число элементов, младший байт первым
|-
| 6–7 || 2 || размерный код, младший байт первым
|}
 
Размерный код: для символьных переменных <code>2 × длина + 1</code> (длина 8 → 17, 64 → 129, 128 → 257, 253 → 507), для числовых 16, для целых 4.
 
===== Порядок записей обратен порядку объявления =====
 
Память под переменные выделяется '''сверху вниз''', от верхней границы, а записи в таблице отсортированы по возрастанию адреса. Поэтому '''первая переменная в <code>DIM</code> оказывается последней записью таблицы''', и сопоставлять их нужно с конца.
 
Проверено на файле <code>EDITOR</code>: 7 операторов <code>DIM</code> объявляют 50 переменных, L1 = 448 = 56 записей. При обратном сопоставлении разность адресов соседних записей совпадает с ожидаемым размером переменной '''у 44 из 50''' точно, а оставшиеся 6 расходятся ровно на 1 байт — это нечётные длины (<code>A¤1</code>, <code>K¤253</code>, <code>G¤19</code>), округляемые вверх до чётной.
 
Размер выделяемой памяти:
 
* скалярная символьная переменная — объявленная длина, округлённая вверх до чётной;
* массив — <code>N × размер элемента + 6</code>, где 6 байт занимает дескриптор.
 
Формула для массивов проверена на 20 массивах <code>EDITOR</code> без единого расхождения: <code>V¤(20)64</code> → 1286, <code>S¤(48)64</code> → 3078, <code>P%(300)</code> → 606, <code>X(2)</code> → 22.
 
Шесть записей таблицы 1, не соответствующих ни одному <code>DIM</code>, находятся в области самых низких адресов, то есть выделены последними. Их тип (<code>59 07</code>, <code>27 01</code>) отличается от остальных; назначение не установлено.


==== Таблицы 2 и 3 ====
==== Таблицы 2 и 3 ====
Строка 249: Строка 284:
Записи по 4 байта: <code>&lt;адрес: 2 байта, младший первым&gt; &lt;флаг&gt; &lt;байт&gt;</code>. Флаг принимает значения <code>10</code>, <code>21</code>, <code>80</code>, <code>81</code>, <code>90</code>, <code>91</code>, <code>A0</code>, <code>A1</code>. Встречаются записи с нулевым адресом — предположительно незанятые слоты. Формат записей в обеих таблицах одинаков, граница между ними определяется только счётчиками пролога.
Записи по 4 байта: <code>&lt;адрес: 2 байта, младший первым&gt; &lt;флаг&gt; &lt;байт&gt;</code>. Флаг принимает значения <code>10</code>, <code>21</code>, <code>80</code>, <code>81</code>, <code>90</code>, <code>91</code>, <code>A0</code>, <code>A1</code>. Встречаются записи с нулевым адресом — предположительно незанятые слоты. Формат записей в обеих таблицах одинаков, граница между ними определяется только счётчиками пролога.


'''Нумерация индексов сквозная''': таблица 1 занимает индексы от 0 до (L1/8 − 1), таблица 2 продолжает нумерацию. В <code>ДЕМ6</code> индексы <code>31</code><code>3F</code> адресуют первые пятнадцать записей таблицы 2, и именно у этих пятнадцати адреса образуют непрерывный ряд с шагом 8 (8-байтовые числа BCD).
Число записей — <code>L2 / 4</code> и <code>L3 / 4</code>: длины в прологе даны в байтах, а запись занимает 4 байта, а не 8.
 
Таблица 2 содержит скалярные переменные, не объявленные в <code>DIM</code>. Их адреса образуют ряд с шагом 8 — под скаляр отводится 8 байт независимо от того, числовой он или целый. Записи с нулевым адресом встречаются вперемешку с обычными; вероятно, это слоты, которым память не выделена.
 
=== Индексы переменных и их имена ===


Оставшиеся записи таблицы 2 в коде не адресуются, а их количество равно числу записей таблицы 1 — 49 в <code>ДЕМ6</code> и 3 в <code>DIG DEM</code>. Похоже, каждой размерной переменной соответствует второй дескриптор в низкой области памяти, однако на третьем файле это разбиение не проверяется, поэтому пока наблюдение, а не правило.
Переменные адресуются в коде однобайтовым индексом. Индексы назначаются '''в порядке первого появления переменной в тексте программы''', а не по алфавиту и не по типу.


{{Внимание|1=Соответствие «индекс переменной в коде → имя переменной» не установлено. В таблицах имена в явном виде отсутствуют. Индексы, судя по наблюдениям, назначаются по порядку объявления и первого появления переменной: в <code>#СТАТИСТ</code> <code></code>=00, <code>N3</code>=0E, <code>N6</code>=11, <code>F1¤</code>=12, <code>E</code>=1B, <code>E7%</code>=1C, <code>U1¤()</code>=30, <code>F%</code>=5C, <code>N7</code>=5D, <code>W0</code>=7D, <code>U9%</code>=81. Это главное препятствие для получения читаемого листинга.
Для переменных, объявленных в <code>DIM</code>, это даёт прямое соответствие: операторы <code>DIM</code> перечисляют свои переменные подряд, в том же порядке, в каком они записаны в исходном тексте. В <code>EDITOR</code> семь операторов <code>DIM</code> занимают индексы <code>01</code><code>32</code> непрерывно; индекс <code>00</code> достался переменной <code>R2%</code>, встретившейся раньше — в строке 30, до первого <code>DIM</code>. В <code>ДЕМ6</code>, где до <code>DIM</code> переменных не было, нумерация начинается с <code>00</code>.


Известно, что имена ограничены буквой с необязательной цифрой — оператор <code>LIST V</code> выводит переменные в алфавитном порядке от <code>A</code> до <code>Z9</code>, группируя их по типам: целые, действительные, символьные, затем массивы целых, действительных и символьных.<ref name="balasanyan" /> Такое имя укладывается в девять бит, что делает вероятным его размещение в паре «флаг + байт» записей таблиц 2 и 3. Порядок записей в таблицах при этом не алфавитный, а соответствует порядку объявления переменных.}}
{{Внимание|1='''Имена переменных в оттранслированном файле не сохраняются.''' Таблицы содержат дескрипторы памяти — адрес, тип, размер, — но не имена.
 
Проверка на <code>EDITOR</code>, где программа сохранена одновременно в текстовом и оттранслированном виде: сопоставление 866 общих строк дало однозначное имя для 146 индексов из 160, и ни одно поле таблиц с этими именами не коррелирует. В таблице 1 поле по смещению 4–5 совпадает у <code>Y¤4</code> и <code>Z¤253</code> (72), у <code>D¤8</code> и <code>Q5¤40</code> (204); в таблице 2 последний байт записи совпадает у заведомо разных переменных (<code>D%</code> и <code>I%</code>, <code>J%</code> и <code>E</code>).
 
Это согласуется с назначением параметра <code>T</code>: при выполнении имена не нужны, всё адресуется по индексу, а отказ от их хранения и даёт заявленную экономию места. Практическое следствие — '''читаемый листинг из оттранслированного файла восстановить нельзя'''. Восстанавливается структура программы, но переменные остаются безымянными, если не сохранилась текстовая версия того же исходника.}}


=== Записи строк ===
=== Записи строк ===
Строка 266: Строка 309:


В отличие от текстового представления, записи '''пересекают''' границу сектора; два служебных байта нового сектора в длину не входят.
В отличие от текстового представления, записи '''пересекают''' границу сектора; два служебных байта нового сектора в длину не входят.
==== Выравнивание в конце сектора ====
Если до конца сектора остаётся слишком мало места, чтобы разместить трёхбайтовый заголовок очередной записи, остаток сектора заполняется нулями, а запись начинается с первого байта следующего сектора. Такие нули стоят '''после''' разделителя <code>FE</code> и в длину записи не входят.
В файле <code>EDITOR</code> (1422 строки) это встречается 9 раз, все — на позициях 248–251 от начала содержимого сектора. Разборщик, не учитывающий выравнивание, теряет синхронизацию именно в этих местах.


=== Операторы ===
=== Операторы ===
Строка 309: Строка 358:
| <code>24</code> || IF || подтверждено
| <code>24</code> || IF || подтверждено
|-
|-
| <code>25</code> || KEYIN ||
| <code>25</code> || KEYIN || подтверждено
|-
|-
| <code>26</code> || ON ||
| <code>26</code> || ON || подтверждено
|-
|-
| <code>27</code> || DEFFN' ||
| <code>27</code> || DEFFN' || подтверждено
|-
|-
| <code>28</code> || PRINTUSING || в опубликованной таблице ошибочно указан GOSUB'; исправлено по данным
| <code>28</code> || PRINTUSING || в опубликованной таблице ошибочно указан GOSUB'; исправлено по данным
Строка 329: Строка 378:
| <code>2E</code> || LIST ||
| <code>2E</code> || LIST ||
|-
|-
| <code>2F</code> || RUN ||
| <code>2F</code> || RUN || подтверждено
|-
| <code>30</code> || RETURN CLEAR || подтверждено
|-
| <code>34</code> || ON ERROR || подтверждено
|-
| <code>3F</code> || <code>%</code> (краткая форма REM) || подтверждено
|-
|-
| <code>35</code> || LET ||
| <code>35</code> || LET ||
Строка 341: Строка 396:
| <code>42</code> || STOP ||
| <code>42</code> || STOP ||
|-
|-
| <code>43</code> || AND( ||
| <code>43</code> || AND( || подтверждено
|-
|-
| <code>44</code> || READ || подтверждено
| <code>44</code> || READ || подтверждено
Строка 349: Строка 404:
| <code>46</code> || DIM || подтверждено
| <code>46</code> || DIM || подтверждено
|-
|-
| <code>47</code> || CONVERT ||
| <code>47</code> || CONVERT || подтверждено
|-
|-
| <code>48</code> || PACK( ||
| <code>48</code> || PACK( || подтверждено
|-
|-
| <code>4A</code> || ADD ||
| <code>4A</code> || ADD ||
Строка 359: Строка 414:
| <code>4C</code> || PRINT || подтверждено
| <code>4C</code> || PRINT || подтверждено
|-
|-
| <code>4D</code> || ROTATE ||
| <code>4D</code> || ROTATE || подтверждено
|-
|-
| <code>4E</code> || COM ||
| <code>4E</code> || COM ||
|-
|-
| <code>50</code> || HEXPRINT ||
| <code>50</code> || HEXPRINT ||
|-
| <code>51</code> || RESTORE || подтверждено
|-
|-
| <code>52</code> || NEXT || подтверждено
| <code>52</code> || NEXT || подтверждено
Строка 387: Строка 444:
| <code>5D</code> || UNPACK( || подтверждено
| <code>5D</code> || UNPACK( || подтверждено
|-
|-
| <code>5E</code> || RETURN ||
| <code>5E</code> || RETURN || подтверждено
|-
|-
| <code>5F</code> || TRACE ||
| <code>5F</code> || TRACE ||
Строка 395: Строка 452:
| <code>62</code> || XOR( ||
| <code>62</code> || XOR( ||
|-
|-
| <code>64</code> || INIT ||
| <code>64</code> || INIT || подтверждено
|-
| <code>66</code> || DATA LOAD BT || подтверждено
|-
| <code>68</code> || DATA SAVE BT || подтверждено
|-
|-
| <code>6D</code> || COPY ||
| <code>6D</code> || COPY ||
|-
| <code>6E</code> || DATA SAVE BA || подтверждено
|-
| <code>70</code> || DATA LOAD BA || подтверждено
|-
| <code>71</code> || DATA LOAD DA || подтверждено
|-
|-
| <code>74</code> || DATA LOAD DC || выведено из данных
| <code>74</code> || DATA LOAD DC || выведено из данных
Строка 407: Строка 474:
| <code>7A</code> || DSKIP ||
| <code>7A</code> || DSKIP ||
|-
|-
| <code>7B</code> || LIMITS ||
| <code>7B</code> || LIMITS || подтверждено
|-
| <code>7D</code> || LOAD DC || выведено из данных
|-
|-
| <code>7E</code> || MOVE ||
| <code>7E</code> || MOVE ||
|-
| <code>80</code> || SAVE DC || выведено из данных
|-
|-
| <code>81</code> || SCRATCH ||
| <code>81</code> || SCRATCH ||
Строка 415: Строка 486:
| <code>83</code> || VERIFY ||
| <code>83</code> || VERIFY ||
|}
|}
Пометка «подтверждено» означает сверку с текстовым листингом той же программы. Основная часть подтверждений получена на файле <code>EDITOR</code> автоматическим сопоставлением 866 общих строк: в строках, где число операторов в обоих представлениях совпало, первый глагол каждого оператора сверялся с первым словом соответствующего оператора текста. Так подтверждены 34 значения, включая <code>21</code> = GOTO (147 совпадений), <code>4C</code> = PRINT (212), <code>24</code> = IF (266).
Отдельно стоит краткая форма комментария <code>%</code> (токен <code>3F</code>): она встречается в <code>EDITOR</code> 122 раза против 7 у полного <code>REM</code> (токен <code>56</code>), и её операнд записывается сырым текстом без префикса <code>E3</code> — так же, как у <code>REM</code>.


Значения назначены не по алфавиту, а тематическими группами с запасом внутри каждой: управление ходом выполнения в 0x21–0x2F, ввод-вывод около 0x41–0x4C, дисковые операции в 0x74–0x83. Составные дисковые команды получают '''один''' токен на всю фразу, тогда как в Wang та же команда собирается из нескольких (<code>DATA</code> + <code>LOAD</code> + <code>DC</code>).
Значения назначены не по алфавиту, а тематическими группами с запасом внутри каждой: управление ходом выполнения в 0x21–0x2F, ввод-вывод около 0x41–0x4C, дисковые операции в 0x74–0x83. Составные дисковые команды получают '''один''' токен на всю фразу, тогда как в Wang та же команда собирается из нескольких (<code>DATA</code> + <code>LOAD</code> + <code>DC</code>).
Строка 434: Строка 509:
|-
|-
| <code>D4</code> || <code>&gt;</code> || вероятно
| <code>D4</code> || <code>&gt;</code> || вероятно
|-
| <code>D5</code>, <code>D6</code>, <code>D8</code> || остальные знаки сравнения || вероятно
|-
|-
| <code>D7</code> || <code>&lt;</code> || подтверждено
| <code>D7</code> || <code>&lt;</code> || подтверждено
Строка 446: Строка 523:
|-
|-
| <code>DE</code> || <code>,</code> || подтверждено
| <code>DE</code> || <code>,</code> || подтверждено
|-
| <code>CC</code> || GOSUB в составе <code>ON … GOSUB</code> || подтверждено
|-
| <code>CD</code> || GOTO в составе <code>ON … GOTO</code> || подтверждено
|-
|-
| <code>E0</code> || ссылка на массив, далее индекс переменной || подтверждено
| <code>E0</code> || ссылка на массив, далее индекс переменной || подтверждено
|-
| <code>E1</code> || функция <code>STR</code> || подтверждено
|-
|-
| <code>DF</code> || <code>TAB(</code> || подтверждено
| <code>DF</code> || <code>TAB(</code> || подтверждено
Строка 458: Строка 541:
|-
|-
| <code>E8</code> || числовая константа: <code>E8 &lt;байт BCD&gt;</code>, диапазон 0–99 || подтверждено
| <code>E8</code> || числовая константа: <code>E8 &lt;байт BCD&gt;</code>, диапазон 0–99 || подтверждено
|-
| <code>E6</code> || <code>OR</code> || подтверждено
|-
|-
| <code>E9</code> || <code>*</code> || вероятно
| <code>E9</code> || <code>*</code> || вероятно
Строка 467: Строка 552:
| <code>FE</code> || разделитель записей строк || подтверждено
| <code>FE</code> || разделитель записей строк || подтверждено
|}
|}
У дисковых операторов (<code>7D</code>, <code>80</code>, <code>81</code>) первый операнд — идентификатор дисковода: <code>0</code> означает <code>F</code>, <code>1</code> — <code>R</code>.


Числовые константы кодируются двумя токенами в зависимости от величины: <code>E8</code> — один байт BCD (0–99), <code>E7</code> — два байта BCD (0–9999). Представление дробных констант в исследованных файлах не встретилось. Номер метки в <code>GOSUB'</code> хранится '''двоичным''', а не в BCD.
Числовые константы кодируются двумя токенами в зависимости от величины: <code>E8</code> — один байт BCD (0–99), <code>E7</code> — два байта BCD (0–9999). Представление дробных констант в исследованных файлах не встретилось. Номер метки в <code>GOSUB'</code> хранится '''двоичным''', а не в BCD.
Строка 521: Строка 608:
# Если байт 9 = <code>21</code>: собрать поток из секторов с байтом 1 = <code>80</code>; прочитать L1, L2, L3; перейти к смещению <code>6 + L1 + L2 + L3</code>.
# Если байт 9 = <code>21</code>: собрать поток из секторов с байтом 1 = <code>80</code>; прочитать L1, L2, L3; перейти к смещению <code>6 + L1 + L2 + L3</code>.
# Для каждой записи: номер строки из двух байтов BCD, длина из третьего, тело до следующего <code>FE</code>. Если по отсчитанной длине не оказалось <code>FE</code> — поток рассинхронизирован, разбор остановить.
# Для каждой записи: номер строки из двух байтов BCD, длина из третьего, тело до следующего <code>FE</code>. Если по отсчитанной длине не оказалось <code>FE</code> — поток рассинхронизирован, разбор остановить.
# После <code>FE</code> пропустить нулевые байты выравнивания, если следующая запись не помещалась в остаток сектора.
# Тело разрезать на операторы по схеме <code>&lt;глагол&gt;&lt;len&gt;&lt;операнды&gt;</code>.
# Тело разрезать на операторы по схеме <code>&lt;глагол&gt;&lt;len&gt;&lt;операнды&gt;</code>.
Обработку файла следует прекращать на '''первом''' встреченном секторе с маркером <code>1C</code>, а не доходить до конца выделенного диапазона: за control record могут лежать остатки предыдущей, более длинной версии файла.
Число записей в таблицах 2 и 3 равно <code>L2 / 4</code> и <code>L3 / 4</code> — запись в них вдвое короче, чем в таблице 1.


Байт длины в каждой записи и в каждом операторе даёт бесплатную проверку целостности на каждом шаге.
Байт длины в каждой записи и в каждом операторе даёт бесплатную проверку целостности на каждом шаге.
Строка 546: Строка 638:
| Текст в программе || ключевые слова в токенах, остальное обычный ASCII || всё в токенах, ASCII только внутри строковых литералов
| Текст в программе || ключевые слова в токенах, остальное обычный ASCII || всё в токенах, ASCII только внутри строковых литералов
|-
|-
| Переменные || имена в открытом виде || индексы в таблицу
| Переменные || имена в открытом виде || индексы в таблицу, имена не хранятся
|-
|-
| Длина оператора || отсутствует || обязательный байт после каждого глагола
| Длина оператора || отсутствует || обязательный байт после каждого глагола
Строка 557: Строка 649:
== Не установлено ==
== Не установлено ==


# '''Имена переменных.''' Главное препятствие для читаемого листинга. Возможный путь — сопоставление токенизированной и текстовой версий одной программы, если обе сохранились на диске.
# Полная таблица токенов: часть значений в диапазонах 0x31–0x33, 0x37–0x3E, 0x49, 0x4F, 0x5B, 0x60, 0x63, 0x65, 0x67, 0x69–0x6C, 0x72, 0x73, 0x76–0x78, 0x7C, 0x7F, 0x82 не занята в известной таблице.
# Полная таблица токенов: часть значений в диапазонах 0x30–0x34, 0x37–0x3F, 0x65–0x78 не занята в известной таблице.
# Представление дробных числовых констант.
# Представление дробных числовых констант.
# Байты 2–7 записи таблицы 1 и байты 2–3 записей таблиц 2 и 3.
# Смысл поля по смещению 4–5 записи таблицы 1 у скалярных переменных: у массивов там число элементов, у скаляров значения повторяются у разных переменных и с именем не связаны.
# Назначение байтов 2–3 записей таблиц 2 и 3 и шести записей таблицы 1 с типами <code>59 07</code> и <code>27 01</code>.
# Точное соответствие индексов переменных записям таблицы 2.
# Природа двухбайтового хвоста без префикса в конце каждого оператора <code>DATA</code> (<code>C9 04</code>, <code>EE 04</code>, <code>00 00</code> в <code>ДЕМ6</code>): в длину оператора он входит, но ни под <code>E7</code>, ни под <code>E8</code> не подходит.
# Природа двухбайтового хвоста без префикса в конце каждого оператора <code>DATA</code> (<code>C9 04</code>, <code>EE 04</code>, <code>00 00</code> в <code>ДЕМ6</code>): в длину оператора он входит, но ни под <code>E7</code>, ни под <code>E8</code> не подходит.
# Причина различия разметки секторов между токенизированными и текстовыми потоками: в текстовых файлах наблюдается позиционная схема <code>02</code>/<code>8F</code>/<code>03</code>, в токенизированных все секторы программы идут с маркером <code>02</code>.
# Причина различия разметки секторов между токенизированными и текстовыми потоками: в текстовых файлах наблюдается позиционная схема <code>02</code>/<code>8F</code>/<code>03</code>, в токенизированных все секторы программы идут с маркером <code>02</code>.
Строка 575: Строка 668:


* Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987. — 264 с.: ил.
* Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987. — 264 с.: ил.
* Реконструкция по образам дисков: файлы <code>TRANSFER</code>, <code>ФС</code> (текстовые), <code>DIG DEM</code>, <code>#СТАТИСТ</code>, <code>ДЕМ6</code> (токенизированные).
* Реконструкция по образам дисков: файлы <code>TRANSFER</code>, <code>ФС</code> (текстовые), <code>DIG DEM</code>, <code>#СТАТИСТ</code>, <code>ДЕМ6</code> (токенизированные), <code>EDITOR</code> (сохранён в обоих представлениях — 182 сектора, 1422 строки; основной материал для проверки структуры таблиц и таблицы токенов).
* Таблица глаголов — из прошивки интерпретатора, по материалам проекта [https://github.com/elaranovikova/iskra226 elaranovikova/iskra226].
* Таблица глаголов — из прошивки интерпретатора, по материалам проекта [https://github.com/elaranovikova/iskra226 elaranovikova/iskra226].
* Аладьев В. З. и др. Персональный компьютер «Искра-226». Архитектура и программное обеспечение: справочное руководство. — Киев, 1988.
* Аладьев В. З. и др. Персональный компьютер «Искра-226». Архитектура и программное обеспечение: справочное руководство. — Киев, 1988.

Текущая версия от 15:40, 12 августа 2026

Этот документ создан для Emuverse и распространяется на условиях лицензии CC-BY-SA-3.0.

Формат файлов BASIC 02 — способ хранения программ на дисках Искра-226. Файловая система унаследована от Wang 2200, представление программы — оригинальное и с Wang несовместимо.

Программа может храниться в двух видах: текстовом (плоский текст в КОИ-8) и токенизированном. Оба вида имеют одинаковую внешнюю структуру и различаются одним байтом в заголовочном секторе.

Общее устройство

На диске располагаются физические сектора размером 128 байт — 77 дорожек, 26 секторов на дорожке, одна сторона. Физические сектора попарно объединены в логические размером 256 байт со сквозной нумерацией. Далее везде будут подразумеваться логические сектора.

Файлу выделяется непрерывный диапазон секторов, границы которого записаны в «Указателе каталога». Раскладка внутри диапазона:

Сектор Содержимое
первый заголовок файла
следующие N поток содержимого
резерв, заполнен нулями
последний выделенный control record

Таким образом, любой файл занимает на диске как минимум на 512 байт больше, чем размер его полезных данных.

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

Искра-226 поддерживает работу с двумя типами файлов: ПФ — «Программные файлы» и ФД — «Файлы данных». Программные файлы всегда содержат программу на Бейсике, в текстовом или упакованном токенизированном виде. Файлы данных, соответственно, данные, доступные из программ пользователя.

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

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

Указатель каталога

Начиная с 0 сектора на диске располагается «Указатель каталога».

Первые 16 байт содержат служебную информацию в следующем составе:

Смещение Размер Значение
0-1 2 размер Указателя каталога в секторах, 16 бит big-endian
2-3 2 номер последнего использованного сектора, 16 бит big-endian
4–5 2 размер тома (обычно 1000), 16 бит big-endian
6–15 10 нули

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

Запись каталога

16 байт, побайтово совпадает с Wang 2200.

Смещение Размер Значение
0 1 статус: 10 — активна, 11 — удалена (scratched), 00 — свободна
1 1 тип файла: 80 — программа, 00 — данные
2–3 2 первый сектор файла, старший байт первым
4–5 2 последний сектор файла, старший байт первым
6–7 2 не используются
8–15 8 имя файла, дополнено пробелами справа

Байт типа не различает текстовое и токенизированное представление: у обоих 80.

Указатель каталога всегда начинается с нулевого сектора диска; за ним идёт область каталога. Число секторов указателя задаётся при создании каталога оператором SCRATCH DISK параметром LS=, по умолчанию равно 24 и в дальнейшем не меняется; допустимы значения от 1 до 255. В нулевом секторе помещается до 15 записей, в остальных — до 16: первые 16 байт нулевого сектора занимает блок параметров диска. Номер сектора указателя, в который попадает имя файла, вычисляется функцией от самого имени, поэтому порядок имён в указателе не совпадает с порядком размещения файлов в области каталога.[1]

Оператор LIST DC выводит типы файлов как P (программный), D (файл данных), SP и SD (вычеркнутые из каталога программный и файл данных соответственно), что соответствует сочетанию байта статуса и байта типа в записи.[1]

Пример

 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
10 80 00 31 00 3F 00 00 44 49 47 20 44 45 4D 20
└───┘ └───┘ └───┘       └─────────────────────┘

Активная запись, программа, секторы 49…63, имя DIG DEM.

Заголовочный сектор

Смещение Значение
0 01
1–8 имя файла, дополнено пробелами
9 признак формата записи, см. ниже
10–255 нули

Байт 9 — единственный признак, по которому различаются два представления программы. Наблюдались значения 20 (текстовое) и 21 (токенизированное), каждое на двух файлах.

Связь с параметрами оператора SAVE DC

Документация[1] описывает три параметра специальных форматов записи программы:

Параметр Действие
T программа записывается в оттранслированной форме — во внутреннем формате машины, что сокращает занимаемое место и время загрузки
P программа защищается от просмотра и записи; после загрузки возможно только выполнение, режим сохраняется до оператора CLEAR
G оттранслированная и защищённая форма одновременно

Отсюда следует, что текстовое представление — это режим по умолчанию, а токенизированное включается явным указанием параметра T. Термин «оттранслированная форма» в документации обозначает именно токенизированную запись.

Документированные значения байта 9 (в источнике он назван «признак защиты»):[2]

Значение Символ Защита Представление
20 пробел нет текстовое
21 ! нет оттранслированное
24 ¤ есть текстовое
25 % есть оттранслированное

Таким образом байт 9 — битовое поле: бит 0 — программа оттранслирована (параметр T), бит 2 — программа защищена (параметр P); сочетание обоих соответствует параметру G. Значения 22 и 23 не используются.

Значения 20 и 21 наблюдались на дисковых образах, 24 и 25 взяты из документации.

Секторы потока

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

Байт 0 — позиция сектора:

Значение Смысл
02 первый сектор потока
8F промежуточный
03 последний

В токенизированных программах все секторы потока наблюдались с маркером 02; позиционная разметка 02/8F/03 подтверждена на текстовых файлах и на потоке значений переменных. Причина различия не установлена.

Байт 1 — только у программных секторов. Во всех исследованных потоках программы он равен 80.


Внимание

У секторов данных служебный заголовок занимает один байт, а не два. То, что во всех наблюдавшихся секторах данных на позиции 1 стоял 00, объясняется не разметкой потока, а тем, что это первый байт двухбайтового идентификатора значения, и 00 в нём означает числовой тип. Для сектора, начинающегося со строкового значения, значение будет другим. Использовать байт 1 как признак «программа или данные» нельзя; надёжный признак — байт 9 заголовочного сектора и байт типа в записи каталога.

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

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

Маркер 03 имеет содержательный смысл: документация[1] описывает файл как область, в которой между последней записью данных и последним сектором файла остаются неиспользованные секторы, а концевая запись помечает сектор, откуда можно продолжать запись. Таким образом 03 — не просто «последний по счёту сектор», а граница записанной области, и всё, что лежит за ней до конца выделенного диапазона, к текущему содержимому файла не относится.

Control record

Лежит в последнем выделенном секторе, а не сразу за содержимым.

1C <длина: 2 байта, старший первым>

Остаток сектора — нули. Маркер 1C соответствует по роли байту 20 в Wang. Второй источник[2] описывает эту структуру как признак конца файла 1C, за которым во втором и третьем байтах сектора записано число реально занятых секторов, а остальные байты нулевые; последний сектор файла данных устроен так же, как последний сектор программного файла.

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

Счётчик соответствует графе «Использовано», которую показывает оператор LIST DC. Согласно документации[1] он не поддерживается системой автоматически: значение заносит прикладная программа оператором DATA SAVE DC END, записывающим односекторную концевую запись в текущий сектор из таблицы устройств. Если признак конца данных не записан, в графе «Использовано» всегда стоит 00001; если записан — число реально использованных секторов.

Там, где значение выставлено, оно равно заголовок + секторы содержимого + сам control record, то есть совпадает с правилом Wang:

Файл Секторов содержимого Счётчик Ожидаемо
ФС 5 7 1+5+1 = 7
ДЕМ6 16 18 1+16+1 = 18


Внимание

Счётчик не является инвариантом файловой системы и при разборе ненадёжен. У #СТАТИСТ он равен 48 при 46 секторах содержимого — значение, по-видимому, осталось от предыдущей, более длинной версии программы, остатки которой лежат в резерве того же файла. У DIG DEM счётчик равен 6 при 5 секторах содержимого, то есть на единицу меньше ожидаемого; возможная причина в том, что там последней операцией была запись потока данных, а не программы. Границы файла следует брать из каталога, а конец данных определять по структуре записей.

Текстовое представление

Программа хранится плоским текстом. Кодировка — КОИ-8 (ГОСТ 19768-74): прописная кириллица в 0xE0–0xFF, строчная в 0xC0–0xDF. Номера строк записаны обычными ASCII-цифрами.

  • Разделитель строк — байт 85.
  • Строка не пересекает границу сектора; хвост сектора заполняется нулями.
  • Байт 24 ($) отображается на экране Искры как ¤.

Выбор 85 возможен именно потому, что кириллица занимает только верхнюю четверть кодовой таблицы и диапазон 0x80–0x9F остаётся свободным. К самой кодировке КОИ-8 это соглашение отношения не имеет и за пределами Искры не встречается.

Разбор: склеить содержимое секторов, отбрасывая по два служебных байта, разбить по 85, отбросить нули.

Пример

02 80 35 20 52 45 4D 20 F0 E5 F4 F2 E5 EE EB EF … 85 38 20 50 52 49 4E 54 …

Заголовок сектора, затем 5 REM ПЕТРЕНКО …, разделитель, 8 PRINT ….

Токенизированное представление

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

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

Пролог и таблицы

Поток начинается с трёх 16-битных длин, старший байт первым, за которыми следуют три таблицы:

Смещение Размер Значение
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

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

Проверено на четырёх файлах:

Файл L1 L2 L3 Старт Первая строка
DIG DEM 24 20 0 50 0 GOTO 10
#СТАТИСТ 216 488 108 818 999 REM КОНЕЦ
ДЕМ6 392 256 0 654 10 REM "ДЕМ6". ГОРОСКОП
EDITOR 448 800 0 1254 10 % EDITOR. ВЕРСИЯ 4.1

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

Таблица 1

Таблица 1 описывает переменные, объявленные в DIM. Запись — 8 байт:

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

Размерный код: для символьных переменных 2 × длина + 1 (длина 8 → 17, 64 → 129, 128 → 257, 253 → 507), для числовых 16, для целых 4.

Порядок записей обратен порядку объявления

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

Проверено на файле EDITOR: 7 операторов DIM объявляют 50 переменных, L1 = 448 = 56 записей. При обратном сопоставлении разность адресов соседних записей совпадает с ожидаемым размером переменной у 44 из 50 точно, а оставшиеся 6 расходятся ровно на 1 байт — это нечётные длины (A¤1, K¤253, G¤19), округляемые вверх до чётной.

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

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

Формула для массивов проверена на 20 массивах EDITOR без единого расхождения: V¤(20)64 → 1286, S¤(48)64 → 3078, P%(300) → 606, X(2) → 22.

Шесть записей таблицы 1, не соответствующих ни одному DIM, находятся в области самых низких адресов, то есть выделены последними. Их тип (59 07, 27 01) отличается от остальных; назначение не установлено.

Таблицы 2 и 3

Записи по 4 байта: <адрес: 2 байта, младший первым> <флаг> <байт>. Флаг принимает значения 10, 21, 80, 81, 90, 91, A0, A1. Встречаются записи с нулевым адресом — предположительно незанятые слоты. Формат записей в обеих таблицах одинаков, граница между ними определяется только счётчиками пролога.

Число записей — L2 / 4 и L3 / 4: длины в прологе даны в байтах, а запись занимает 4 байта, а не 8.

Таблица 2 содержит скалярные переменные, не объявленные в DIM. Их адреса образуют ряд с шагом 8 — под скаляр отводится 8 байт независимо от того, числовой он или целый. Записи с нулевым адресом встречаются вперемешку с обычными; вероятно, это слоты, которым память не выделена.

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

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

Для переменных, объявленных в DIM, это даёт прямое соответствие: операторы DIM перечисляют свои переменные подряд, в том же порядке, в каком они записаны в исходном тексте. В EDITOR семь операторов DIM занимают индексы 0132 непрерывно; индекс 00 достался переменной R2%, встретившейся раньше — в строке 30, до первого DIM. В ДЕМ6, где до DIM переменных не было, нумерация начинается с 00.


Внимание

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

Проверка на EDITOR, где программа сохранена одновременно в текстовом и оттранслированном виде: сопоставление 866 общих строк дало однозначное имя для 146 индексов из 160, и ни одно поле таблиц с этими именами не коррелирует. В таблице 1 поле по смещению 4–5 совпадает у Y¤4 и Z¤253 (72), у D¤8 и Q5¤40 (204); в таблице 2 последний байт записи совпадает у заведомо разных переменных (D% и I%, J% и E).

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

Записи строк

Программа — последовательность записей, разделённых байтом FE. Перед первой записью разделителя нет.

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

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

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

Выравнивание в конце сектора

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

В файле EDITOR (1422 строки) это встречается 9 раз, все — на позициях 248–251 от начала содержимого сектора. Разборщик, не учитывающий выравнивание, теряет синхронизацию именно в этих местах.

Операторы

Тело записи — последовательность операторов:

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

Здесь len — длина только операндов, сам байт len не считается. len = 00 означает оператор без операндов, например голый PRINT.

Наличие явной длины у каждого оператора позволяет разрезать программу на операторы, ещё не зная значений токенов. Эта особенность вынужденная: в отличие от Wang, где многословные команды собираются из нескольких токенов вперемешку с обычным ASCII и границы видны по самому потоку, у Искры весь текст программы переведён в токены и поток непрозрачен.

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

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

20 00 54
23 01 23                          GOSUB ' 35
4C 39 E3 1F «БУДЕТЕ …» DD 00 DD E3 12 «-[1] ИЛИ ДВУМЯ [2]» 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

Сумма: 3 + 59 + 3 + 5 + 8 + 5 = 83, плюс байт длины = 84.

Таблица токенов

Глаголы

Основной массив значений восстановлен из прошивки интерпретатора (алфавитный список ключевых слов по смещению 0x1840 и 51-байтовый массив перестановки по 0x19B6). Отмеченные записи проверены или исправлены сопоставлением токенизированной и текстовой версий одной программы.

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

Пометка «подтверждено» означает сверку с текстовым листингом той же программы. Основная часть подтверждений получена на файле EDITOR автоматическим сопоставлением 866 общих строк: в строках, где число операторов в обоих представлениях совпало, первый глагол каждого оператора сверялся с первым словом соответствующего оператора текста. Так подтверждены 34 значения, включая 21 = GOTO (147 совпадений), 4C = PRINT (212), 24 = IF (266).

Отдельно стоит краткая форма комментария % (токен 3F): она встречается в EDITOR 122 раза против 7 у полного REM (токен 56), и её операнд записывается сырым текстом без префикса E3 — так же, как у REM.

Значения назначены не по алфавиту, а тематическими группами с запасом внутри каждой: управление ходом выполнения в 0x21–0x2F, ввод-вывод около 0x41–0x4C, дисковые операции в 0x74–0x83. Составные дисковые команды получают один токен на всю фразу, тогда как в Wang та же команда собирается из нескольких (DATA + LOAD + DC).

Документация[1] описывает также операторы DATA SAVE DC END, DATA SAVE DC CLOSE и DATA LOAD DC OPEN. Последний соответствует найденному токену 75, поэтому остальные с высокой вероятностью занимают свободные значения в диапазоне 0x70–0x78 — при разборе новых файлов имеет смысл проверять этот участок в первую очередь.

Операнды

Токен Значение Достоверность
00~BF ссылка на переменную по индексу подтверждено
D0 ) вероятно
D1 TO подтверждено
D3 THEN, далее 2 байта BCD — номер строки подтверждено
D4 > вероятно
D5, D6, D8 остальные знаки сравнения вероятно
D7 < подтверждено
D9 = подтверждено
DB # подтверждено
DC / вероятно
DD ; подтверждено
DE , подтверждено
CC GOSUB в составе ON … GOSUB подтверждено
CD GOTO в составе ON … GOTO подтверждено
E0 ссылка на массив, далее индекс переменной подтверждено
E1 функция STR подтверждено
DF TAB( подтверждено
E2 шестнадцатеричный литерал HEX(…): E2 <len> <len байт> подтверждено
E3 строковый литерал: E3 <len> <len байт КОИ-8> подтверждено
E7 числовая константа: E7 <2 байта BCD>, диапазон 0–9999 подтверждено
E8 числовая константа: E8 <байт BCD>, диапазон 0–99 подтверждено
E6 OR подтверждено
E9 * вероятно
EA + вероятно
EB ( вероятно
FE разделитель записей строк подтверждено

У дисковых операторов (7D, 80, 81) первый операнд — идентификатор дисковода: 0 означает F, 1R.

Числовые константы кодируются двумя токенами в зависимости от величины: E8 — один байт BCD (0–99), E7 — два байта BCD (0–9999). Представление дробных констант в исследованных файлах не встретилось. Номер метки в GOSUB' хранится двоичным, а не в BCD.

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

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

01 32 D0        →  01(32)
2A 03 E8 01 D0 D0  →  2A(03,1)

Токен EB используется только как группирующая скобка в выражениях:

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

Разборщик, ожидающий EB перед индексом массива, рассинхронизируется.

Поток значений переменных

Секторы, следующие за программой в токенизированном файле, содержат значения переменных, записанные в том же формате, что и обычные файлы данных. Структура описана в документации[1].

Служебная информация в секторе двух видов:

  • идентификатор сектора — 1 байт в начале сектора, значение 02 (признак данных);[2] под данные остаются 255 байт;
  • идентификатор значения — 2 байта перед каждым значением, содержат тип (числовое или символьное) и длину.

Размеры значений:

Тип Значение Со служебными байтами Значений в секторе
целое 2 байта 4 байта 63
действительное 8 байт 10 байт 25
символьное длина значения длина + 2

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

Пример

В файле DIG DEM сектор данных начинается с идентификатора сектора 02, за которым идут 25 записей по 10 байт:

00 08 <8 байт значения>

Идентификатор 00 08 означает числовое значение длиной 8 байт. Двадцать пятая запись заканчивается на пятом байте от конца сектора, оставшиеся 5 байт не используются: 1 + 25 × 10 + 5 = 256. Расчёт сходится точно и подтверждает однобайтовую длину идентификатора сектора.

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

  1. Прочитать запись каталога: первый и последний сектор, тип должен быть 80.
  2. Прочитать первый сектор: проверить байт 0 = 01, взять имя, прочитать байт 9.
  3. Если байт 9 = 20: склеить содержимое секторов, разбить по 85, отбросить нули, перекодировать КОИ-8. Готово.
  4. Если байт 9 = 21: собрать поток из секторов с байтом 1 = 80; прочитать L1, L2, L3; перейти к смещению 6 + L1 + L2 + L3.
  5. Для каждой записи: номер строки из двух байтов BCD, длина из третьего, тело до следующего FE. Если по отсчитанной длине не оказалось FE — поток рассинхронизирован, разбор остановить.
  6. После FE пропустить нулевые байты выравнивания, если следующая запись не помещалась в остаток сектора.
  7. Тело разрезать на операторы по схеме <глагол><len><операнды>.

Обработку файла следует прекращать на первом встреченном секторе с маркером 1C, а не доходить до конца выделенного диапазона: за control record могут лежать остатки предыдущей, более длинной версии файла.

Число записей в таблицах 2 и 3 равно L2 / 4 и L3 / 4 — запись в них вдвое короче, чем в таблице 1.

Байт длины в каждой записи и в каждом операторе даёт бесплатную проверку целостности на каждом шаге.

Отличия от Wang 2200

Wang 2200 Искра-226
Запись каталога 16 байт совпадает побайтово
Маркер control record 20 1C
Маркер заголовка 40 (50 для защищённых) 01
Маркеры секторов битовое поле в старшей тетраде отдельные значения 02/8F/03
Конец строки в программе 0D 00 00 FE как разделитель
Конец блока FD (EOB) / FE (EOD) не используется
Диапазон токенов 0x80–0xFB, 124 значения глаголы 0x21–0x83, операнды 0xD0–0xEB
Текст в программе ключевые слова в токенах, остальное обычный ASCII всё в токенах, ASCII только внутри строковых литералов
Переменные имена в открытом виде индексы в таблицу, имена не хранятся
Длина оператора отсутствует обязательный байт после каждого глагола

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

Вероятная причина расхождения — кодировка. В Wang верхняя половина таблицы свободна и отдана под ключевые слова; у Искры она занята кириллицей, поэтому глаголы перенесены вниз, а от литерального ASCII в потоке отказались полностью.

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

  1. Полная таблица токенов: часть значений в диапазонах 0x31–0x33, 0x37–0x3E, 0x49, 0x4F, 0x5B, 0x60, 0x63, 0x65, 0x67, 0x69–0x6C, 0x72, 0x73, 0x76–0x78, 0x7C, 0x7F, 0x82 не занята в известной таблице.
  2. Представление дробных числовых констант.
  3. Смысл поля по смещению 4–5 записи таблицы 1 у скалярных переменных: у массивов там число элементов, у скаляров значения повторяются у разных переменных и с именем не связаны.
  4. Назначение байтов 2–3 записей таблиц 2 и 3 и шести записей таблицы 1 с типами 59 07 и 27 01.
  5. Точное соответствие индексов переменных записям таблицы 2.
  6. Природа двухбайтового хвоста без префикса в конце каждого оператора DATA (C9 04, EE 04, 00 00 в ДЕМ6): в длину оператора он входит, но ни под E7, ни под E8 не подходит.
  7. Причина различия разметки секторов между токенизированными и текстовыми потоками: в текстовых файлах наблюдается позиционная схема 02/8F/03, в токенизированных все секторы программы идут с маркером 02.
  8. Токены операторов DATA SAVE DC END и DATA SAVE DC CLOSE.

Примечания

  1. 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987. — 264 с.: ил. — разделы 5.1, 5.2, 11.5, 18.1, 18.4, 18.7.
  2. 2,0 2,1 2,2 Аладьев В. З. и др. Персональный компьютер «Искра-226». Архитектура и программное обеспечение: справочное руководство. — Киев, 1988. — структура программного файла и файла данных, значения признака защиты.

Источники

  • Баласанян В. Э., Богдюкевич С. В., Шахвердов В. А. Программирование на микроЭВМ «Искра 226». — М.: Финансы и статистика, 1987. — 264 с.: ил.
  • Реконструкция по образам дисков: файлы TRANSFER, ФС (текстовые), DIG DEM, #СТАТИСТ, ДЕМ6 (токенизированные), EDITOR (сохранён в обоих представлениях — 182 сектора, 1422 строки; основной материал для проверки структуры таблиц и таблицы токенов).
  • Таблица глаголов — из прошивки интерпретатора, по материалам проекта elaranovikova/iskra226.
  • Аладьев В. З. и др. Персональный компьютер «Искра-226». Архитектура и программное обеспечение: справочное руководство. — Киев, 1988.
  • Структура файловой системы Wang для сопоставления — Wang 2200 Disk Organization.