Распиновка карты памяти ps1

Делаем бесконечную карту памяти для PS1


PS1 (она же PSX, она же PS One) это первое поколение игровых консолей PlayStation от Sony и относится к пятому поколению игровых консолей вообще. Она использует 2х скоростной привод для чтения CD. Такой большой объём данных по меркам актуального для приставки времени позволял игроделам особо не оглядываться на ограничения при создании контента для игр, что делало последних более качественными, по сравнению с играми предыдущего поколения приставок. А ещё, игры теперь могут быть длинными. И если любая игра за редким исключением на консолях предыдущих поколений вполне себе могла быть пройдена за одну игровую сессию, то с играми PS1 всё обстояло иначе. Для сохранения прогресса у PlayStation предусмотрены карты памяти: маленькие сменные модули энергонезависимой памяти.

Если вам интересно, как именно устроена карта памяти PlayStation 1, как она работает и как можно создать свою — добро пожаловать под кат.

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


Фотография печатной платы стандартной карты памяти на 15 блоков

Как видно из фото, устройство карты очень простое: контроллер, который обслуживает запросы системы, и, собственно, энергонезависимая память, которая представлена стандартной NOR FLASH. Логически, карта памяти разбита на 15 блоков, которые игры могут использовать. Может показаться, что 15 не логично для двоичной системы, но тут противоречия нет: один блок отдан под файловую систему, там хранятся имена файлов и даже анимированные иконки, прям как потоки у NTFS. Каждый блок имеет размер 8 КиБ, 16 блоков в сумме это 128 КиБ, что и видно по маркировке FLASH памяти на фото выше.

На первых порах этого хватало всем, но потом стали появляться игры, которые использовали более одного блока за раз. Например, некоторые симуляторы, вроде Sega GT, используют 4-5 блоков, а Constructor так вообще всю карту памяти на 15 блоков. Это вынуждало покупать больше карт и ситуация грозила стать как с дискетами или картриджами. Но потом подтянулись пираты и стали выпускать карты на 2, 4 или 8 страниц разом. И переключались страницы либо по хитрой комбинации на джойпаде, либо явной кнопкой на самой карте памяти. Правда, в картах более 2х страниц применялось сжатие, и фактическое число страниц было значительно меньше, а некоторые карты могли тупо заблокироваться. И вывести их из этого состояния было очень трудно, но на что только не шли игроки ради своих сохранений. Вот типичные представители многостраничных карт памяти:


Слева карта памяти на 2 страницы, справа на 8. У правой есть аппаратная кнопка перелистывания страниц и индикатор, отображающий число от 1 до 8, который скрыт за тёмным стеклом

Небольшое лирическое отступление.

Всё началось в 2001м году, когда я купил чудо диск для ПК под названием «Все эмуляторы», на котором были эмуляторы PS1 в том числе: это были Bleem! и ранний ePSXe. И мой тогдашний комп даже смог играбельно запускать мои диски от PS1! А чуть позже у меня появился модем и я узнал про DirectPad Pro. Подключение родного джойстика к компьютеру (пусть и через LPT) многого стоит. И работала эта система как на 9х так и на XP! А ещё чуть позже, уже в 2002м я узнал про Memory Card Capture Sakura! Эта программа позволяла работать с настоящими картами памяти, используя всё ту же схему подключения DirectPad Pro. Именно тогда у меня появилась идея сделать «бесконечную» карту памяти, которая бы позволяла обмениваться информацией с компьютером без необходимости дополнительных устройств. Но на тот момент у меня не было достаточно информации и доступной элементной базы, и идея оставалась лишь идеей, теплясь где-то на задворках сознания.

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

Физический интерфейс.

Итак, карта памяти и джойпады работают через общий интерфейс. Количество сигналов в нём 6, вот их названия и назначения:

  • SEL0 – Сигнал выбора первого порта, активный уровень низкий
  • SEL1 – Сигнал выбора второго порта, активный уровень низкий;
  • CLK – Тактовый сигнал интерфейса, пассивное состояние высокий уровень, по спаду сдвиг, по фронту защёлкивание;
  • CMD – Сигнал данных от консоли к периферии;
  • DAT – Сигнал данных от периферии к консоли;
  • ACK – Аппаратный хэндшейк, активный уровень низкий.

Так же на интерфейсе присутствует два разных напряжения питания: 3.3в и 7.6в. Все сигналы, кроме SEL0 и SEL1 являются общими для всех подключаемых устройств. Именно поэтому нерабочая карта памяти или джойпад во втором слоту влияли на рабочие в первом, хотя после 16ти битных приставок это казалось странным. Я думаю, что многие уже узнали в интерфейсе стандартный SPI – всё верно, так и есть. Только добавлен сигнал ACK для подтверждения операции ввода/вывода. Вот назначения сигналов на контактах карты памяти:


Отремонтированная карта памяти с 5ти вольтовым FLASH

Технические характеристики интерфейса такие:

Измеренная частота сигнала CLK является 250кГц, что составляет 4 мкс на период. С физическими параметрами интерфейса разобрались, теперь транспортный уровень. Опытный инженер уже заметил, что джойпад и карта памяти подключены полностью параллельно и могут конфликтовать между собой. Так и есть, для этого присутствует программный арбитраж. После активации сигнала SELn периферия продолжает молчать, но слушает первый присланный байт. Если этот байт равен 0x01, то далее активируется джойпад, а карта памяти продолжает молчать до деактивации сигнала выбора. А если байт был 0x81, то всё наоборот: карта памяти активируется, а джойпад молчит. Естественно, что хост ждёт сигнала ACK на этот байт арбитража и ждёт недолго. Это нужно для того, чтобы успеть опросить остальную периферию, если часть этой периферии отсутствует. Дело в том, что операционная система опрашивает контроллеры и карты памяти строго по сигналу обратного хода луча, или более известного как VBlank. Так принято, что игры в приставках до 5-го поколения завязаны на этот тайминг, который равен частоте кадров. А частота кадров строго стабильна и нормирована: 50Гц для PAL и 60Гц для NTSC. Т.е., период опроса джойстиков и карт памяти равен 20мс для PAL или 16мс для NTSC.

Итак, с арбитражем разобрались, теперь собственно верхний уровень. Какие команды понимает стандартная карта памяти PS1? Да, собственно, их всего 3.

  • R – 0x52 или Read. Чтение сектора карты памяти;
  • W – 0x57 или Write. Запись сектора карты памяти;
  • S – 0x53 или Status. Чтение статуса карты памяти.

Вся карта памяти разбита на сектора. Один сектор 128 байт. Таким образом, в 128КиБ помещается 0x400 или 1024 сектора. При этом стирания сектора при записи не нужно. Но система гарантированно даёт время на целый следующий кадр при записи. Т.е., читать карту памяти она может каждый кадр, а записывает через один. К слову, всякие «Взломщики кодов» для ускорения не придерживаются данных таймингов. Разберём каждую команду более детально.

Протокол работы с картой памяти.

Легенда:
CMD — Данные, которые хост посылает карте.
DAT — Данные, которые карта посылает хосту.
FLAG — Текущие флаги состояния карты и результат предыдущей команды.
PRV — Предыдущие принятые данные, результат упрощения схемы в карте.
MSB — Старший байт номера сектора.
LSB — Младший байт номера сектора.
DATA — Полезные данные.
CHK — Контрольная сумма блока.
ACK — Флаг подтверждения.

Байт флагов FLAG использует следующие биты:

  • D5 – Устанавливается некоторыми картами памяти не от Sony. Назначение неизвестно.
  • D3 – Устанавливается при подаче питания и сбрасывается при любой записи. Используется для обнаружения смены карты памяти.
  • D2 – Устанавливается при ошибках записи, актуален на следующее обращение после самой операции.

После подачи питания FLAG равен 0x08. И после первой же записи он обнуляется. Операционная система PS1 всегда делает запись в сектор 0x003F для этого, тем самым вызывая износ этого сектора. Но в рамках разметки карты памяти системой какой-либо полезной информации в этом секторе нет. Номер сектора MSB:LSB 10 бит и составляет число от 0x0000 до 0x03FF. Контрольная сумма CHK это обычный XOR всех 128 байт данных + MSB и LSB. Подтверждение ACK может принимать всего 3 значения: G 0x47, E 0x43 и 0xFF. G = Good или «ОК». E = Error. Собственно, при чтении из карты ACK всегда равен G, а при записи G = ОК, E = ошибка контрольной суммы а 0xFF означает неправильный номер сектора. Правда, большинство карт просто откидывают неиспользуемые биты в старшем байте номера сектора и поэтому никогда не отвечают 0xFF. Числа 0x0400 и 0x0080 в команде статуса наводят на определённые мысли, что это количество секторов и размер сектора в байтах, но доподлинно это не известно. Ну вот мы и мы подошли к главному:

Реализация своей карты памяти.

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

  1. При чтении необходимо время на актуализацию данных. Между номером сектора и фактической передачей данных у нас есть 4 байта, у которых мы можем немного растянуть ACK. К слову, у оригинальной карты памяти на NOR FLASH все ACK идут равномерно, у карт памяти с SPI FLASH после передачи LSB происходит задержка ACK, во время которой контроллер выставляет команду в SPI FLASH и вычитывает первый байт, а остальные он вычитывает по ходу обмена.
  2. При записи после передачи всего пакета и начала самой записи в массив требуется время, но тут система сама даёт необходимую задержку.

Что касается питания, то у джойпадов 3,3в используется для логики а 7,6в для питания моторчиков. У карт памяти обычно используется только одно питание. Если внутри стоит 5в FLASH, то используется 7,6в и стабилизатор. Если стоит 3,3в FLASH, то используется сразу 3,3в.

Первый вариант я собрал на STM32F407VG, который питается от 3,3в, имеет SPI для PSIO, быстрый SDIO и достаточно памяти, чтобы хранить весь образ внутри себя, решая вышеупомянутые проблемы. Фотография готового устройства:


Первая версия моей карты памяти на STM32F407

Получилось быстро, надежно, но дорого. А можно сделать дешевле? Ну, что-ж, вызов принят. Учитывая специфику задачи, я выбрал STM32F042F6. Вот что получилось:


Вторая версия моей карты памяти на STM32F042

Карта у нас ведомая, поэтому стабилизация частоты внешним кварцевым резонатором не нужна, достаточно внутреннего генератора. Аппаратный SPI у этого контроллера один, поэтому я его отдал SD карте, чтобы снизить задержки на транспорт. PSIO тут будет программный.

Программная реализация.

Карта инициализируется на скорости 375кГц (PCLK/128), а работает на 24МГц (PCLK/2). При таких скоростях замеры показали, что SDv1 и SDHC отдают сектор в рамках 2,8мс на всю транзакцию полностью. Это следует запомнить, т.к. важно для операции чтения PSIO.

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

Код достаточно комментирован и я думаю, что в особом разборе не нуждается. Отмечу лишь тот момент, что после получения второго байта номера сектора в команде чтения мы устанавливаем флаг для операции чтения с SD карты для кода, который крутится в вечном цикле функции main(). И сразу после этого 4 следующих ACK выдаются с удлинённым временем. В интерфейсе это выглядит вот так:


Скриншот из программы логического анализатора, выделяются 4 большие задержки в транзакции

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

В вечном цикле постоянно анализируется сигнал вставления SD карты. Если её вытащить на ходу, то код отключит PSIO и PS1 «потеряет» карту. Если же карту вставить обратно (или просто подать питание со вставленной картой), то сначала будет попытка инициализировать карту функцией Card_Init(), которая вернёт тип обнаруженной карты. Это важно, потому что у SDv1 и остальных SDHC/SDXC адресация идёт различными методами. Сам код инициализации никаких секретов не несёт и подсмотрен в куче доступных в интернете примеров про FatFS и подобных проектов.
Следом за инициализацией карты вызывается хитрая функция Card_FSInit(). Это – самая главная фишка данного проекта. Дело в том, что STM32F042 скромный по возможностям и потянуть полную поддержку FatFS на необходимой скорости не сможет. Поэтому, я придумал такой метод: файл образа у нас всегда 128КиБ, поэтому, необходимо знать только 256 секторов по 512 байт, в каждом из которых будет ровно 4 сектора нашей карты памяти PS1. Таким образом, мы делаем следующее:

  1. Анализируем сектор LBA=#0 на предмет MBR. Если это действительно MBR, то получаем новый сектор, где находится MBS.
  2. Получив адрес предполагаемого MBS (это может быть #0, если нет MBR или какое-то число, если MBR есть) мы начинаем его анализ на предмет принадлежности одной из FAT: FAT12, FAT16, FAT32 или vFAT.
  3. Если сектор прошёл проверку, то мы забираем из него информацию о структуре и в корневом каталоге ищем элемент с именем файла. В данном случае это ‘MEMCRD00.BIN’.
  4. Если такой файл находится, то проверяем его размер – он должен быть строго фиксирован 0x20000 байт. Если всё так – получаем номер первого кластера.
  5. Если мы дошли до этого пункта, то у нас уже есть вся необходимая информации для построения списка физических LBA секторов, где расположен наш образ. Пробегая по цепочке FAT и используя информацию о структуре из MBS, заполняем таблицу из 256 номеров LBA секторов.

В случае успеха запускается PSIO и PS1 увидит свою карту как обычную. Если на каком-либо этапе произошла ошибка, то работа прерывается, загораются оба светодиода и всё остаётся в таком состоянии до снятия питания или замены SD карты. Вот код этой процедуры:

Скажу честно, так как это всего лишь PoC, то здесь реализован поиск только у FAT16. FAT12, наверное, и не надо поддерживать – microSD таких малых объёмов не бывает. А вот FAT32 или vFAT добавить возможно, если это кому-нибудь понадобится в будущем.

Имя образа ‘MEMCRD00.BIN’ выбрано не случайно. Дело в том, что в будущем я планирую добавить выбор образа через стандартную для многостраничных карт памяти комбинацию кнопок на джойпаде: при зажатом SELECT следует однократное нажатие на L1/R1. И меняя последние 2 символа можно поддержать 100 образов в корневой директории, от ‘MEMCRD00.BIN’ до ‘MEMCRD99.BIN’. Для этого есть задел в обработчике прерывания по SCK в интерфейса PSIO, ветка где анализируется обращение к джойпаду. Сделать сниффер проблем нет, но периферия контроллеров у PS1 богатая и придётся практически всех их поддерживать.

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

Источник

Adblock
detector