bkread
Утилиты работы с файлами БК001x-xx

Давным-давно
(в восьмидесятые годы)
почти в другой галактике
(в СССР) жил странный советский
народ. Все у этого народа было не как у людей.
И главным отличием этих существ была неодолимая любовь к трудностям...

БК0010 - первый (и последний) советский массовый домашний компьютер.
Вместе с компьютером потребитель получал интерпретатор "языка высокого уровня" Фокал (изобретенного в лабораториях DEC в шестидесятые годы) с которым шли первые компьютеры PDP (легендарной PDP-11 ещё не родилось, а Фокал уже был)
или, так называемый, "Вильнюсский" Бейсик, поставляемый с более поздними моделями,
а еще позднее и то и другое (Фокал в "блоке МСТД").
Единственным устройством для хранения программ и данных, предусмотренным заводом-изготовителем для этого чуда советской техники был бытовой магнитофон, на который через примитивный аудиовыход БК со скоростью ~1200 бод можно было записывать информацию. И пользователи копили эти программы бобинами, стопками кассет МК-60, на которых-же и производился обмен программами между ними.
Постепенно, времена и приоритеты менялись - некоторые пользователи просто забросили свои БК в пользу более прогрессивных РС, а некоторые обзавелись контроллерами дисководов и перешли к работе на них. А к середине девяностых про БК, похоже, окончательно забыли. Теперь с БК реально уже никто не работает. Но, я уверен, бобины и кассеты с записанными на них произведениями остались практически у всех бывших пользователей БК. И иногда хочется посмотреть, что же на них...


Содержание: Однажды мне стукнула в голову мысль "сохранить для потомков" свои программы, которые я писал для БК пятнадцать лет назад. И я с удивлением ;) обнаружил, что не имею возможности прочесть данные с моих бобин ни на одном из имеющихся у меня навороченных компьютеров - ни в одном из них почему то не предусмотрено БК-совместимого магнитофонного интерфейса. И это повергло меня в пучину ПНИР (Псевдонаучно-исследовательской работы), целью которой было обучение компьютера чтению данных с магнитофонных лент и дальнейшее приведение этих данных в удобочитаемый вид, а результатом - представленные ниже утилиты.
Очевидно, что снятие информации с магнитофонных лент возможно только через аудиокарту и очевидно (мне по крайней мере), что программы считывания и преобразования следует писать под один из вариантов Unix, чтобы обеспечить их переносимость. Первым кандидатом на такую ОС оказался Solaris, лучший вариант Unix на момент создания версии 1 (да и сейчас). Через некоторое время потребовалось поддтвердить тезис о пользе Unix c точки зрения переносимости. и была создана версия 2, обеспечивающая работу как под Solaris, так и под Linux. Вот, что, собственно, было сделано и выставляется сейчас для всеобщего рассмотрения.
  • Внимание! Данные, излагаемые мною далее получены при анализе отрывков нескольких руководств по БК, схеме компьютера, дизассемблированных кодов Монитора БК и осциллограмм снятых с выходов БК. Поэтому они не претендуют на полноту и точность. Хотя, на основе этих данных и созданы успешно функционирующая программы, сведения, очевидно, не свободны от огрех. Я выражаю особую признательность П. Б. Эльтерману за предоставленные им личные записи и результаты его экспериментов с драйвером магнитофона, которые позволили серьезно ускорить эту работу.
  • Мне так-же хотелось бы выразить отдельную благодарность Кириллу Бухарову за проявленное им терпение при тестировании Linux-версий программ.
  • Особое Внимание! ОС Linux является наименее документированным и наиболее "глюкавым" вариантом Unix на сегодняшний день. Автор снимает с себя всякую моральную ответственность за возможные повреждения вашей информационной среды (в виде, например, падающих модулей ядра) в случае использования bkread под этой ОС. В настоящее время тестирование проводилось под следующими клонами Linux и звуковыми системами: 
    • Red Hat Linux 7.1 - OSS и ALSA 0.9.0
    • Red Hat Linux 7.3 - OSS
    • Debian GNU/Linux 2.2 - OSS

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

Логически данные (записанный файл) выглядят на ленте так:
 
Стартовая синхропоследовательность Маркер начала Заголовок Данные Концевая синхропоследовательность

С точки зрения читающей программы стартовая настроечная последовательность представляет собой 100008 нулевых одиночных импульсов (один импульс - один период сигнала см. рис.). Длина (продолжительность) такого импульса в дальнейшем определяет "эталонную единичную длину" для всех последующих данных.

Импульс
Рисунок. Одиночный импульс.

Маркер начала это 108 нулевых одиночных импульсов, заголовок - 248 байт информации о записаном файле и следующие за этим 108 нулевых импульсов, данные - это, собственно, данные файла, заканчивающиеся двухбайтовой контрольной суммой, концевая настроечная последовательность - 4008 нулевых одиночных импульсов. Все элементы, кроме массива данных, заканчиваются синхроимпульсами, за которыми следует единичный бит (синхропоследовательностью).

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


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

Рисунок. Единичный бит (реально).
Реально, при записи амплитуда единичного импульса понижается вдвое соответственно, в идеале при воспроизведении его размах также должен быть меньше (см. рис.). Однако, за счет нелинейности частотных характерисик трактов магнитофонов и выходного тракта БК, при считывании можно считать что амплитуда импульсов одинакова и, скорее всего именно поэтому драйвер магнитофона БК так и запрограммирован.

Рисунок. Единичный бит (идеально).
Синхроимпульс представляет собой одиночный импульс утороенной длины. Данный тип импульса в комбинации с единичным битом образует синхропоследовательность и используется для обозначения границ элементов, образующих файл на ленте. Синхроимпульс также передается с половинной амплитудой. Синхропоследовательность, образующую разделительный элемент между полями в файле можно видеть на рисунке.

Рисунок. Синхропоследовательность.
Фактически, синхропоследовательность это последовательность из импульсов тройной, двойной, а, затем, единичной длины.
Биты записываются в порядке увеличения разряда в байте, т.е. нулевой бит - первым, седьмой - последним. Для примера на следующем рисунке приведена последовательность, импульсов образующая комбинацию битов "0101" в виде, выводимом драйвером БК.

Рисунок. Последовательность бит "1010"
На рисунке обозначены значения каждого импульса. При этом "1D" и "0D" означают стартовые (значащие) импульсы в двухимпульсных посылках, кодирующих единичный и нулевой биты соответственно, а "0S" означает синхроимпульс (по длительности совпадающий с "0D").

Теперь можно рассмотреть поля файла отдельно.

Внимание! Данные длиной в слово хранятся и записываются в БК в формате LSB (младший байт первый), что следует учитывать при написании или портировании утилит работы с файлами под системы, использующие MSB формат (большинство RISC систем).

Как уже указывалось, в начале файла записывается настроечная последовательность из 100008 нулевых одиночных импульсов, которая используется для детектирования начал файла и настройки ПО на скорость воспроизведения данных. В конце настроечной последовательности записывается синхропоследовательность.

Маркер начала, как это следует из косвенных данных к которым у меня был доступ, должен содержать параметры записи (типа, инверсии данных, порядка байт и пр.). Однако, так-как в реальных условиях никакие опции не используются, и не заложены в алгоритмы работы драйвера магнитофона БК, можно считать, что он содержит 108 нулевых одиночных импульсов и синхропоследовательность в конце.
Заголовок содержит следующие поля:
 

Байты Записанная информация
0-1 Адрес памяти, с которого записан файл
2-3 Длина области данных
(без контрольной суммы)
4-248 Имя файла
Таким образом, максимальная длина имени файла - 16 символов (байт). Если имя файла содержит меньше 16 символов, ПО БК, как правило, заполняло остающиеся байты пробелами (408). Заголовок записывается сплошным потоком бит, никаких разделителей байтов не предусмотрено. В конце заголовка записывается 108 нулевых импульсов и синхропоследовательность.

Данные так же записываются сплошным потоком бит без разделителей байтов в потоке.

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

        MOV    START, R1
        MOV    LENGTH, R2
        CLR    R0
LOOP:   MOVB   (R1)+, R3
        BIC    #177400, R3     ; СБРОС СТАРШИХ БИТ В СЛУЧАЕ ПЕРЕНОСА ЗНАКА
        ADD    R3, R0
        ADC    R0              ; УЧЕСТЬ ВОЗНИКНОВЕНИЕ ПЕРЕНОСА
        SOB    R2, LOOP
        ...                    ; R0 СОДЕРЖИТ КОНТРОЛЬНУЮ СУММУ

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

Назначение конечной синхрополедовательности мне не известно и не понятно.



Алгоритм чтения данных с ленты на основе вышеизложенного получается довольно простым, хотя и содержит несколько неочевидных моментов, суть которых рассмотрена после описания:
  1. Анализировать сигнал, пока на входе не появится группа из NSTART однородных импульсов.
  2. Настроить временные интервалы на основе среднего из NTUNE последующих интервалов.
  3. Продолжить чтение импульсов до синхропоследовательности.
  4. Прочесть маркер начала и убедиться в его правильности.
  5. Прочесть заголовок и убедиться в его правильности.
  6. Прочесть данные.
  7. Подсчитать контрольную сумму и убедиться в правильности считывания данных.
  1. Константы NSTART и NTUNE в оригинальном драйвере равны 40008 и 2008 соответственно. Хотя искуственное разделение непрерывной настроечной последовательности может показаться неразумным, связано оно с определенными особенностями функционирования контроллера DRAM на КР1801ВП13, которые при работе программы записи на МЛ могут в начале последовательности ускорить работу ЦП за счет временного уменьшения времени доступа к оперативной памяти как результата работы алгоритмов (все временные задержки при записи формируются программными циклами), в результате чего при чтении могли бы неправильно быть вычислены счетчики, ответственные за измерение длительности импульсов. Однако, при чтении фактически пропускается NSTARTимпульсов, что гарантирует прохождение интервала времени, достаточного для стабилизации скорости, а счетчики формируются уже на основе последующих NTUNE импульсов.
  2. Видимо для упрощения, в оригинальном драйвере длина маркера не контролируется, алгоритм пропускает все импульсы единичной длины до появления синхропоследовательности.
  3. Аналогично, в оригинальном драйвере не проверяется значение следующего за синхроимпульсом бита в синхропоследовательности, алгоритм просто читает (и пропускает) следующий за синхроимпульсом бит.


Утилитаbkread - основная программа пакета осуществляет чтение с магнитной ленты файлов, записанных по алгоритмам, принятым в БК. Хотя программа функционирует согласно оригинального алгоритма БК, написана она была без знания оного, на основе анализа осциллограмм. Затем, некоторую ясность внесли чтения смутных мануалов к компьютеру, что дало возможность сделать программу полностью рабочей, а появление материалов Эльтермана - закрепить полученное, исправить мелкие недочеты и довести программу до "публичного" вида. Не могу не отметить, что при помощи bkread  удалось перенести на современные носители файлы со всех доступных автору лент.

bkread читает информацию с магнитофона через аудиовход. Вызов утилиты производится следующей командой:

$ bkread [ключи] [имя]
после этого программа приступает к чтению информации с ленты. Если параметр имя отсутствует, то все встречающиеся при воспроизведении файлы сохраняются, в текущей директории, если задано определенное имя, то сохраняются только файлы с совпадающим именем. В случае, если в директории уже имеются файлы с таким именем то к имени прибавляется префикс ".nnn", где nnn - возрастающее число, гарантирующее от записи файла поверх существующих.
В командной строке можно задавать следующие ключи:

$ ./bkread -v -r 80 -m 20 -e 132 MONITOR
Opening /dev/dsp as sound device...
Opening /dev/mixer as mixer device...
Recording level set by "IGain" (12)
Monitoring device is "Line " (6)
Fragment size set to 4096 bytes
Audio: recording gain = 60
Audio: monitor gain = 40
Audio: sample_rate = 44100
Audio: edge level = 128
Analyzer: synchro tuning pattern count = 1632
Analyzer: max. number of adaptations set to unlimited

Listening for header synchros (S=1536; T=96)...
Synchros detected. Pattern length = 7.
Pattern length after tuning = 8.
Patterns lengths: 0 = 8; 1 = 16.

Signal Period Analyzer Line (2..40):
--000000000111111111111SSSSSSSSSSSSSSSS
---------------------------------------
000000001111111111222222222233333333334
234567890123456789012345678901234567890

File Name = "MONITOR         "
Data Length =  20000 (octal).
Data Address = 100000 (octal).
Tape Checksum =  17341 (octal).
Data Checksum =  17341 (octal).
Saving file to: MONITOR (8214 bytes).

Listening for header synchros (
S=1536; T=96)...
^CSignal (2) received. Program stopped.

Думаю, вывод утилиты с ключем -v достаточно мнемоничен, чтобы его не комментировать. Работа прерывается по сигналу SIGINT (в терминах Unix), в данном случае этот сигнал вызывался нажатием комбинации Ctrl-C (см. termio(7I)).
Программа сохраняет файлы под именами, заданными в заголовках на МЛ. Оконечные пробелы отбрасываются. Если файл с таким именем уже имеется на диске, bkread записывает его под тем-же именем, добавляя ".n" в конец (n последовательно увеличивается до тех пор, пока файла с таким именем не обнаружится на диске).
К bkread идет еще небольшой скрипт bk2bin, призванный конвертировать считанные файлы в формат, понимаемый некоторыми эмуляторами БК. Основная цель создания данного скрипта, была несколько иная - показать насколько просто это сделать. Автор приглашает разработчиков эмуляторов и ПО для БК к сотрудничеству для написания других конверторов.



Утилита dasm1 представляет собой простейший вариант дизассемблера из набора команд процессора КР1801ВМ1 в вид текстовой формат, близкий к формату MACRO-11(tm), позволяющий сравнительно легко разбираться в двоичных кодах программ для БК. Фактически, dasm представляет собой несколько доработанный вариант дизассемблера, который я в написал в 1988 году для ДВК-2, на которой мне довелось работать в то время.
Командная строка для вызова dasm следующая:
$ dasm [-a | -b | -c | -f forced_file] tape_file
Где ключ -a, -b и -c задают формат дополнительного вывода данных: При всех заданых ключах (dasm -a -b) вывод принимает следующий вид:
; Disassembler V01.5.1 BK 001x-xx (bkread)
; Alexander "las" Lunev
; Start address = 100000
; Length = 20000
; Tape file name: MONITOR           
;       Command Operand(s)              Address  Codes
;
$START$:JMP     MONITOR                 !100000: 000167 000254 167 000 254 000 "w.?."
 EMT_4P:.WORD   100742                  !100004: 100742 342 201 "??"
 EMT_6P:.WORD   101010                  !100006: 101010 010 202 ".?"
Непосредственно за кодами инструкций, после восклицательного знака (комментарий) следуют: обязательный адрес, пословная расшифровка кода команды, ее байтовые коды, (все в восьмеричном исчислении) и ее строковое представление.

Ключ -f задает файл форсированных меток, т.е. если (как правило ;) дизассемблер неверно интерпретирует участки кода как данные или как коды, когда они таковыми не являются, то можно форсировать интерпретацию данных в нужном представлении.

Сам файл устанвок имеет достаточно примитивный формат:

<type> <addr> <label>
...
Пустые строки и комментарии в файле не допускаются; type - тип форсируемой области, допустимые значения c (code) и d (data); addr - адрес начала области (восьмеричный); label - имя для метки, которая будет обозначать начало области. Легко видеть, что приведенном выше примере вывода dasm был использован файл форсированных меток содержащий следующие строки:
d 100004 EMT_4P
d 100006 EMT_6P
Длина имени метки - не более 7 символов.

Внимание! dasm - достаточно примитивная утилита, которая не будет совершенствоваться или исправляться в дальнейшем, ввиду подготовки к выходу более совершенного JDasm, идущего в составе JavaPDP. Это, однако не мешает любителям программирования на C модифицировать и дополнять программу с целью приведения ее в "божеский вид".


Утилита focal2txt, как и следует из имени переводит файлы из внутреннего представления Фокала в текстовой вид.
Командная строка для вызова focal2txt следующая:
$ focal2txt tape_file
Утилита выводит программу в текстовом представлении в стандартный вывод. Никаких преобразований кодировок не производится, поэтому для просмотра или печати программ следует использовать те или иные перекодирующие утилиты. В "ближайшее время" готовится к выходу Java-апплет для просмотра программ на языках Фокал БК-0010 и Бейсик БК-0010, за дополнительной информацией обращайтесь по адресу imbecyle@mail.ru.


Для компиляции утилит следует сначала подправить Makefile под параметры ваших ОС и компилятора С, а затем, командой
$ make
создать исполняемые модули.
Теоретически, данные программы должны работать под любой UNIX-подобной ОС, хотя проверялись они мной только под Solaris 7 и 8 (Intel и SPARC) и некоторыми вариантами Linux с компиляторами gcc. Я буду рад новостям относительно любых новых патформ (кроме, пожалуй, Windows), на которые удалось перенести эти утилиты или появлению для них графических wrapper-ов, скажем, на Tcl/Tk. Все сообщения о переносах, ошибках и модификациях программ прошу присылать на мой E-Mail адрес - imbecyle@mail.ru.
А. Лунев (las)
2000-2004