Введение
Одна из самых известных историй о защите гибких дисков связана с Dungeon Master. Эта игра, выпущенная в декабре 1987 года, сочетала в себе усложнённый формат физического диска (нечёткие биты) со скрытными проверками защиты, встроенными в сам геймплей.
Рекомендую прочитать эту статью, в которой представлен замечательный обзор гибких дисков, после которого идёт очень подробный обзор защиты нечёткими битами диска Dungeon Master для Atari ST. Есть также вот эта замечательная статья, которая более подробно рассказывает об историях, связанных с защитой Dungeon Master. В ней есть цитата одного из авторов Dungeon Master:
«Мы получили преимущество, получив патент на схему защиты гибких дисков, в которой для записи дисков требовалось специализированное оборудование за 40 тысяч долларов. Без этого оборудования образ диска создать было невозможно, а само оборудование было выведено из эксплуатации».
Причина такой высокой цены, скорее всего, заключалась в той точности таймингов, которая необходима для надёжного создания нечётких битов. В те времена, когда большая часть мира измеряла производительность в микросекундах, требуемая точность измерялась в наносекундах.
Компьютер BBC Micro имел процессор 6502 на 2 МГц и для выполнения его простейших инструкций требовалось два такта, то есть 1 микросекунда. Есть ли надежда, что можно будет записать нечёткие биты в условиях таких ограничений? Посмотрим, как далеко нам удастся зайти. Эта работа будет называться «проектом Oiled Otter».
Чтобы вы почувствовали дух времени, вот изображение аппарата дублирования гибких дисков 3,5″. Удивительно, насколько он похож на фотокопировальный аппарат, только в приёмник вместо бумаги вставляются диски! Похоже, такую машину даже могла продать вам компания Advanced World Products.
Пользовательский порт BBC Micro
BBC Micro был известен своей превосходной расширяемостью, в том числе и с помощью так называемого «пользовательского порта» (user port). Этот порт управляется 6522 Versatile Interface Adapter, работающим с частотой 1 МГц. Сам порт имеет 8 контактов данных и 2 контакта управления. Эти контакты обеспечивали очень высокую степень контроля. Контакты данных можно было по отдельности настраивать как вводы и выводы, а логическим уровням выводов можно было задавать высокий или низкий сигнал.
Почему же нам важен user port? Мы попытаемся управлять дисковым приводом непосредственно через него. Устранив из уравнения контроллер гибких дисков, мы, возможно сможем от него избавиться и добиться более прямого контроля над дисковым приводом и передаваемыми потоками данных.
Кабель от user port к дисковому приводу
На изображении выше показан мой кабель, подключающий user port к дисковому приводу. Разъёмы стандартны, а соединяющие их провода — это просто перемычки. Я искренне стремлюсь создать нечто, что можно было изготовить «в те дни», поэтому не использую никакую дополнительную электронику.
Кабель устроен следующим образом:
Главный вывод, который мне удалось извлечь из этой схемы — интерфейс дискового привода, вероятно, проще, чем можно было подумать. Мы можем управлять дисковым приводом и запрашивать его важное состояние всего на 8 контактах. Всё очень просто. Допустим, нам нужно раскрутить привод, тогда достаточно подать низкий сигнал на PB0 и PB1. Если вы хотите дождаться, пока диск довращается до начала дорожки, то нужно запрашивать логический уровень на PB6, пока мы не увидим изменение уровня сигнала с высокого на низкий. Для пошагового движения достаточно задавать логический уровень «step in» противоположно «step out», а затем выполнять пульсацию низкого сигнала на контакте «step».
Пока всё отлично, у нас есть базовый контроль над приводом, но мы ещё ничего не записывали.
Проблемы с электрикой
Обязательно краткое отступление о проблемах с электрикой, потому что я с ними столкнулся. При подключении случайных пар компонентов они могут заработать, но иногда требуются ухищрения. Вот интервал напряжений, изначально наблюдавшийся на контакте W/DATA привода:
Пытаемся записывать импульсы на привод с частотой FM в 250 кГц
Напряжение логики 1 равно примерно 3,4 В, а напряжение логики 0 — примерно 1,5 В. Это серьёзная проблема! Приемлемые уровни напряжения TTL чётко заданы:
«Входящий сигнал TTL считается „низким“, если имеет напряжение от 0 В до 0,8 В относительно заземляющего вывода, и „высоким“ при напряжении от 2 В до VCC (5 В). Если на вход элемента TTL подаётся сигнал напряжения в интервале от 0,8 В до 2,0 В, то элемент не даёт конкретного ответа, а поэтому сигнал считается „неопределённым“».
Напряжение логики 0, равное 1,5 В, считается «неопределённым», и не вызовет никаких действий. И в самом деле, мой привод с этим сигналом ничего не записывал.
Проблему удалось устранить снятием с привода оконечного резистора кабеля гибких дисков. Вот фотография моего привода, где сборка оконечных резисторов обведена красным:
Это замечательно решает проблему уровней напряжения, после чего всё работает. Похоже, что многие порты BBC Micro кроме дискового порта не имеют достаточно сил для управления кабелем с оконечными резисторами. Но постойте-ка — вероятно, для установки этого резистора была какая-то причина? Да. Его снятие имеет два хитрости:
- Обращайте внимание на длину кабелей. Без оконечного резистора длинные кабели подвержены искажениям сигнала.
- Следите за уровнями напряжения на неподключенных проводах. Я наблюдал напряжение 1,32 В на контакте привода S/SEL (выбор стороны). Это ненормально, потому что такое значение тоже находится в интервале неопределённости TTL. Куда привод будет записывать данные? Может быть, на верхнюю сторону, может быть, на нижнюю. А может и вообще ни на одну из них! Проблема была решена подключением каждого значимого кабеля и подачей на них высокого или низкого сигнала.
Нужно повысить пропускную способность
Проблема, которую мы пока обходили стороной: как подавать сигнал на контакт W/DATA? Это «жёсткий» контакт. Он имеет высокую пропускную способность и обладает точными требованиями к таймингам. Давайте на секунду перестанем мечтать о нечётких битах с наносекундной точностью, и попробуем записать на привод простые FM-импульсы.
Большинство дисков для BBC Micro имеют формат FM (они же DFM, они же одиночной плотности), кодируемый с частотой 250 кГц. Запись дорожки FM на самом деле выполняется довольно просто. Нужно проверить, вращается ли привод и открыта ли створка записи. После этого каждые 4 микросекунд или выполняем пульсацию W/DATA на низкий, а потом обратно на высокий сигнал (бит 1), или не делаем этого (бит 0). Чаще всего каждый второй бит должен быть равен 1 (синхронизирующий бит для поддержания тайминга и синхронизации).
Управление W/DATA через процессор — безнадёжная задача. 4 микросекунды — это 8 тактов процессора; этого определённо не хватит для загрузки байта, его сдвига, записи 0, а потом 1 в логические уровни user port. Простой цикл скорее всего займёт 12 с лишним микросекунд, а это слишком много. Так что для достаточно быстрой записи W/DATA нам придётся воспользоваться возможностями чипа 6522 VIA.
Регистр сдвига 6522 VIA
Самый очевидный кандидат для нашей задачи — это регистр сдвига. Регистр сдвига — это 8-битный регистр. В соответствующем режиме загрузка регистра сдвига заставит чип последовательно передать 8 бит по одному из контактов user port. Это отлично — биты обрабатываются параллельно с работой основного процессора, поэтому процессор может спокойно тратить время на создание нового набора битов, сдвиг которых нужно будет начать.
К сожалению, мне не удалось заставить работать эту схему. Единственный сдвиговый режим, имеющий потенциал достаточно быстрой работы — это «сдвиг с тактовой частотой системы». В спецификации Western Design Center 6522 есть хорошая схема:
Системный тактовый генератор VIA имеет частоту 1 МГц, поэтому тактовая частота сдвига будет сигналом 500 кГц, а разрешение выводимых битов равно 250 кГц. Этого как раз хватает. Однако я не разобрался, как заставить непрерывно и плавно работать тактовую частоту сдвига. Даже после попыток точного тайминга для перезагрузки регистра сдвига интервал контакта тактовой частоты сдвига всегда выглядела вот так:
Получается, что в единственном сдвиговом режиме, достаточно быстром для наших опытов, перезагрузка регистра сдвига вызывает задержку перед продолжением сдвига. Это нам не подходит.
Режим импульсного вывода 6522 VIA
Малоизвестной функцией 6522 является его «режим импульсного вывода». Он описывается в спецификациях не всех вариаций 6522, но вот небольшая запись о нём в спецификации MOS Technology:
Наконец-то мы нашли спецификацию, точно описывающую его поведение. Этот режим очень интересен для нас, потому что одна операция записи в VIA обещает привести к двум отдельным действиям: на выходной контакт подаётся низкий логический сигнал, а спустя 1 такт (1 микросекунду) он возвращается к высокому сигналу без усилий с нашей стороны. Благодаря этому мы можем использовать его для управления выходным сигналом 250 кГц. Ресурсы процессора очень ограничены — циклом решить задачу никак не получится, но линейный блок кода 6502 сможет справиться, например:
&70 points to &FE60, aka. user 6522 VIA ORB register.
STA (&70),Y 8 cycles, pulse output
STA (&70),Y 8 cycles, pulse output
STA (&70),Y 8 cycles, pulse output
LDA (&70),Y 8 cycles, do not pulse output
STA (&70),Y 8 cycles, pulse output
...
Это сработает. На это у процессора как раз хватит мощи. 8 тактов — это 4 микросекунды, то есть кратчайшее время между импульсами диска.
К сожалению, такая операция чрезвычайно активно задействует память. Для каждого закодированного бита FM требуется 2 байта линейного кода 6502. Каждый значимый бит данных — это два бита FM, потому что каждый второй бит является синхронизирующим. Дорожка состоит из 3125 байт, поэтому потребуется 3125 * 8 * 2 * 2 == 100 кБ линейного кода. BBC Micro имеет 32 кБ ОЗУ, так что здесь нам не повезло. Можно записывать одиночные (маленькие) секторы, в том числе и мощные новые механизмы защиты диска. Но мы не сможем записывать большие (1024 байта) секторы или полные дорожки. Для правильной записи большого количества дисков требуются обе эти операции. Более того, разрешение таймингов равно 1 микросекунде, чего недостаточно для записи множества более сложных защит и поверхностей дисков.
Мы можем быть довольны, что получилось заставить работать хоть что-то, учитывая такие ограничения, но это решение нас не совсем устраивает.
Помощь от ничего не обещающего порта вывода
К счастью, я общаюсь с умными людьми, например, с Bitshifters Collective. (Сходите оценить их последнее демо Evil Influences!) В беседе в Slack Том Седдон (автор эмулятора b2) предложил… использовать вывод порта RGB (?)
Кабель-переходник с видео на диск… такое не встретишь на Amazon каждый день.
Поначалу я посмеялся над этой идеей, но чем больше размышлял, тем вероятной она мне казалась. В BBC Micro использует для таймингов видеочип 6845. Как и 6522, это капризный процессор, но, по крайней мере, его особенности хорошо разобраны благодаря демо Bitshifters, в которых нещадно эксплуатируется 6845. Кроме того, я занимался реверс-инжинирингом, чтобы заставить эмулятор jsbeeb правильно эмулировать Hitachi 6845. Давайте посмотрим в этом видео на работу Oiled Otter, а потом расскажем, что увидели:
Всё работает благодаря необычному конфигурированию чипа 6845. 6845 работает с частотой 1 МГц, а тайминг кадров настроен так, чтобы на «кадр» приходилась одна растровая строка на 32 микросекунд/32 байта. При выводе каждого кадра регистры видеопамяти 6845 перезаписываются для получения следующих 32 байтов из потенциально другого места. То есть каждые 32 микросекунд из таблицы паттернов вывода выбирается другой паттерн вывода. Мы сконфигурировали контакты RGB на передачу 8 пикселей в микросекунду, то есть 256 на паттерн вывода. Это даёт нам огромное количество различных возможных паттернов вывода. Но так как мы записываем фрагменты по 32 микросекунд дисковых кодировок FM, подходят нам только несколько паттернов. В 32 микросекунд мы можем уместить 8 импульсов/битов FM. 4 битов будут синхронизирующими, и обычно все они равны 1. 4 бита будут битами данных, и у них существует всего 16 комбинаций.
Например, если мы записываем полубайт данных 0x5, то 32-микросекундный вывод должен выглядеть так:
Видеоданные будут иметь вид 00FFFFFFFFFFFFFF00FFFFFF00FFFFFF00FFFFFFFFFFFFFF00FFFFFF00FFFFFF. Первый, второй, четвёртый и пятый 00 — это синхронизирующие биты. Между синхронизирующими битами расположен битовый паттерн данных 0101, или 0x5.
Ограничения процессора и памяти хорошо сбалансированы. В конечном итоге, эта схема похожа на ту, которую бы мы примерили с регистром сдвига VIA, если бы она сработала: какой-то небольшой сопроцессор (видеочип) занимается передачей набора битов FM, а центральный процессор свободен для загрузки и предоставления следующего паттерна. Требования к памяти вполне разумны. Таблица из необходимых выходных фрагментов на 32 микросекунд благодаря специальному линейному режиму адресации вполне помещается в 1024 байтов. Список индексов поиска для всей дорожки примерно равен 12 кБ, то есть всё отлично помещается в 32 КБ ОЗУ BBC Micro.
Особенности BBC Micro / 6845
Последний символ/столбец 6845
Разумеется, чтобы заставить эту схему работать, нельзя не столкнуться с некоторыми «забавными» особенностями. Первая из них — это особенность 6845, из-за которой он выводит чёрный цвет для последнего символа каждой растровой строки. Это проклятие разработчиков демо, а теперь, похоже, и исследователей дисков. Вот слайд из недавнего доклада, на котором я демонстрировал эту проблему:
Слева показан эффект из демо, испорченный вертикальными чёрными полосами, вызванными проблемой «последнего чёрного символа/столбца». Несколько растровых строк 6845 размещаются в одном растровом проходе, и, к сожалению, ненамеренно появляются чёрные полосы. При управлении диском эффект намного хуже: чёрные полосы заменяются на нежелательные импульсы, которые записываются на диск.
Справа показано изображение решения: передаваемая на диск волна просто инвертирована. Теперь нормально, что последний столбец всегда будет чёрным (показан оранжевым контуром), потому что там всегда требуется нулевое значение. Строго говоря, это нарушает требования к таймингам некоторых дисковых приводов к пульсации низких сигналов W/DATA. Вот схема таймингов привода той эпохи, Mitsubishi M4852/M4853:
Согласно этой схеме, логический 0 должен удерживаться до 2100 нс. При инвертированной форме сигнала следует ожидать от 3000 нс и более. Однако приводы, которые у меня есть, волнуют только спадающие импульсы данных, а не их длительность. Это неудивительно. Можно было бы проделать пару трюков, чтобы избежать особенностей 6845 и обеспечить время длительности в соответствии со спецификацией, но это оказалось необязательным, а потому я этим не занимался.
Порча DRAM
Порча DRAM (DRAM decay) — это кошмар. Она происходит, когда нам не удаётся вовремя обновить DRAM. Цитата из статьи Википедии про обновление памяти:
«Этот процесс выполняется автоматически в фоновом режиме электронной схемой памяти и невидим для пользователя».
Это справедливо для современных систем, но не для BBC Micro. На BBC Micro обновление DRAM является побочным эффектом подсистемы видео. При нём используется то свойство, что стандартные экранные режимы итеративно обходят все строки DRAM за короткий промежуток времени. Вероятно, вы уже догадались, к чему всё идёт — наш особый видеорежим, используемый для вывода кадров на 32 микросекунды, не является стандартным экранным режимом. Он не гарантирует, что обойдёт все строки DRAM, поэтому происходит порча DRAM! Порча DRAM — это не шутки. Из-за незапланированной порчи DRAM я терял различные программы и содержимое дисков. Покажу вам ради смеха программу на BASIC, вызывающую порчу DRAM для самой себя всего за долю секунды:
Печально в порче DRAM то, что если она вас постигла, то вы запросто можете потерять данные.
С другой стороны, если вы ожидаете порчу DRAM, то обычно можете с лёгкостью её обойти. В случае Oiled Otter есть различные критически важные циклы, при которых видеочип 6845 находится в необычном состоянии. Для сохранения обновления DRAM при каждом из таких циклов работает ручной инкремент получения данных из памяти.
Открывшиеся возможности
Теперь, когда у нас есть работающая система записи на диск в обход контроллера гибких дисков, что мы можем с ней сделать? В показанном выше видео мы уже продемонстрировали её способность записи произвольных дисков с кодировкой FM.
Но в этом исследовании нам очень повезло. Благодаря недостаткам регистра сдвига VIA нам пришлось искать решение с контактами видеовыхода и мы получили доступ к гораздо более мелкому разрешению таймингов на контакте W/DATA. Мы используем MODE4 компьютера BBC Micro, задействующий пиксельную тактовую частоту 8 МГц. Это означает, что можно каждые 125 нс выводить чёрные или белые пиксели, переключая импульсы записи с разрешением 125 нс. Если бы мы хотели потратить чуть больше дополнительной памяти (которая у нас есть) на таблицы побольше, то могли бы использовать MODE0, задействующий пиксельную тактовую частоту 16 МГц, что обеспечивает разрешение 62,5 нс. Я убедился, что 125 нс вполне достаточно для всех протестированных дисковых защит, но здорово, что у нас осталось пространство для манёвра.
Защита длинными дорожками
Моя любимая дисковая защита — защита длинными дорожками. Она была популярна во времена Amiga. Мне не кажется, что её когда-нибудь использовали на BBC Micro. Мне нравится защита длинными дорожками потому, что она очень фундаментальна: контроллер гибких дисков имеет большой допуск по изменению скоростей записи (потому что дисковые приводы вращаются с разными скоростями), но записывает он только на одной правильной скорости.
Более сложная защита длинными дорожками заключается в записи двух секторов на одну дорожку, при которой один из секторов записывается с большей скоростью. Проверка защиты от копирования заключается во времени, которое заняло считывание этих двух секторов. Сектор, записанный с большей скоростью, должен считываться значительно быстрее.
Может ли Oiled Otter записать такую дорожку? Да, и довольно легко. Учитывая разрешение вывода в 125 нс, можно легко создать несколько элементов таблицы вывода, похожими на обычные, но с вырезанными из каждой 1 микросекунды 125 наносекундами. Вот видео создания защиты длинными дорожками и проверки считывания диска:
Защита нечёткими битами
Наверно, нам давно пора вернуться к тому, с чего мы начали: к защите нечёткими битами. Может ли Oiled Otter создавать нечёткие биты на оборудовании 1981 года? Давайте попробуем. Вот изображение результатов пары считываний сектора после его записи командой FUZZ системы Oiled Otter.
Команда FUZZ записывает полубайт 0x8, а бит данных поступательно откладывается на инкременты по 125 нс. Это похоже на описание того, как записывались нечёткие биты Dungeon Master. Как видно из скриншота, байты данных 0x88 вскоре начинают считываться неверно и недетерминированным образом. Но дисперсия не на 100% случайна, как «слабые биты» (weak bit) — дисперсия заключается в том, достаточно ли поздно записан бит 0x8, чтобы иметь вероятность быть пропущенным. Если он пропущен, мы всё равно можем увидеть, что в этом безумии есть паттерны и логика.
Представленные выше результаты являются применением принципов нечётких битов к данным, закодированным FM. В кодировке FM каждый бит данных перемежается синхронизирующим битом. Это приводит к тому, что иногда тактовые биты проникают в поток данных (см. байты 0xFF в первом прогоне — скорее всего, это тактовые биты). Защита Dungeon Master использует нечёткие биты в сочетании с MFM. Это приводит к более простой ситуации, где нечёткие биты перемещаются между двумя валидными кодировками битов данных и не задевают синхронизирующие биты! Разумеется, Oiled Otter может записывать MFM, GCR и любую другую кодировку, которую только можно придумать. Всё это просто разные протоколы одного фундаментального примитива — возможности передачи импульса приводу в любой момент времени с хорошим разрешением.
Чтобы правильно его оценить, приведём вид нечётких битов на диске с осциллографа. Максимумы довольно неравноерны, а когда два импульса очень близки друг к другу (1 микросекунда или около того, слишком близко по стандарту любой кодировки), распознанная приводом сила перемагничивания даже начинает ослабевать.
Миссия выполнена
Мы получили возможность записи дисковых импульсов с разрешением 125 нс. Этого совершенно достаточно для создания сложных дисковых защит, в том числе длинных дорожек, слабых битов и нечётких битов. Совсем неплохо для железа 1981 года со скоростью выполнения самой быстрой команды в 1 микросекунду!