Снимаем образы с картриджей для Dendy/Famicom/NES

Снимаем образы с картриджей для Dendy/Famicom/NES

1164

В сети просто можно отыскать и ROM’ы этих самых игр. Часто люди качают их и даже не думают, каким же образом кто-то в один прекрасный момент прочел их из картриджа. В данной статье я и постараюсь поведать, как же это делалось в случае с NES/Famicom, которая у нас была больше известна как «Денди», и покажу, как можно сделать это без помощи других. Ни для кого не секрет, что на данный момент можно просто скачать эмулятор практически хоть какой игровой консоли 80х-90х и поиграть в классические игры на компе , телефоне и почти всех остальных платформах.

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

Видео:

(Ссылкой: www.youtube.com/watch?v=gPSpk2gbAD4)

Статья:
Во-первых, в картридже сходу два типа памяти: с кодом игры, и с изображениями из игры. Почти все сходу же произнесут, что это просто ROM-память с параллельным доступом, и ничего сложного в её чтении быть не обязано, но это не совершенно так. Итак, как же работает картридж у Famicom? Таковым образом картридж является чем-то вроде оперативной памяти, куда уже загружена игра. Любая из их врубается прямо в шину данных консоли. 1-ая — параллельно с оперативной памятью и процессором (CPU), а 2-ая параллельно с видеопамятью и видеочипом (PPU).

Разглядим же распиновку слота картриджа, и как он работает.

Слева — передняя часть. Вид на консоль сверху.

→ CPU A0-A14 — контакты, через которые задаётся адресок для чтения CPU памяти
↔ CPU D0-D7 — контакты, через которые мы передаём данные CPU памяти
→ PPU A0-A13 — контакты, через которые задаётся адресок для чтения PPU памяти
↔ PPU D0-D7 — контакты, через которые мы передаём данные PPU памяти
→ M2 — местный clock-сигнал, воспринимает высочайший уровень, когда идёт обращение к CPU памяти
→ /ROMSEL — логический NAND меж M2 и CPU A15, который недоступен впрямую
→ CPU R/W — описывает, тип операции: высочайший уровень — чтение, маленький — запись
← /IRQ — дозволяет картриджу генерировать прерывание, снутри консоли подтянут к +5В
→ PPU /RD — воспринимает маленький уровень, когда консоль читает PPU память
→ PPU /WR — воспринимает маленький уровень, когда консоль пишет в PPU память
→ PPU /A13 — просто напросто инвертированный сигнал от PPU A13
← CIRAM A10 — дозволяет картриджу определять принцип зеркалирования видеопамяти в консоли
← CIRAM /CE — при низком уровне включает видеопамять снутри консоли
→ Звук (вход) — здесь в картридж идёт звук с аудиочипа
← Звук (вход) — здесь из картриджа идёт звук в том виде, в каком мы его уже слышим
* Земля и питание — без комментариев, напряжение 5 вольт

Сейчас подробнее, незначительно технической инфы.
Т.е. Чтение либо запись выбираются через CPU R/W. Обратите внимание, что при этом у нас нет контакта CPU A15, который должен отвечать на старший разряд адреса. Потому традиционно его можно впрямую подключить к /CE ноге ROM-памяти. CPU память консоли лежит в спектре меж 0 и $FFFF (16 бит адресации). К картриджу традиционно относятся адреса $8000-$FFFF. Заместо него есть /ROMSEL, который воспринимает маленький уровень лишь в случае, когда M2 и теоретический CPU A15 сразу принимают высочайший уровень. Да много для чего, но о этом ниже. Для чего нужна запись в картридж? когда консоль читает либо пишет в адреса $8000-$FFFF.

PPU память имеет адреса от 0 до $3FFF (14 бит адресации), к картриджу при этом традиционно относится 0-$1FFF. Конкретно в этом спектре хранятся изображения, и это может быть как ROM, так и RAM, но картридж сам описывает, какие адреса относятся к нему, а какие к внутренней части консоли, конкретно для этого употребляется CIRAM /CE. В старенькых играх это было агрессивно задано перемычкой на плате, в наиболее новейших традиционно может изменяться программно во время игры. Раздельно стоит огласить про CIRAM A10 — этот контакт описывает, как зеркалируется память в спектре меж $2000 и $2FFF снутри консоли. память консоли активизируется, когда A13 равно единице — в спектре от $2000 до $3FFF. Обратите внимание, что снутри Famicom и NES памяти ниже $2000 и нет совсем, она должна быть в картридже. Традиционно это принципиально найти в зависимости от того, как в игре происходит движение — вертикально либо горизонтально. У PPU употребляются отдельные контакты для чтения и записи: PPU /RD и PPU /WR. Традиционно (практически постоянно) его замыкают впрямую на PPU /A13, т.е.

В современных китайских «Денди» и иных клонах их тоже не припаивают. Само собой, звуковой чип из картриджа никак не сдампить. В NES этих контактов уже не было. Использовалось это изредка, но дозволяло сделать музыку в играх еще приятнее за счёт доп синтезаторов звука. Да, в уникальном Фамикоме были ещё аудиовход и аудиовыход, что дозволяло картриджу быть доп источником звука.

У NES принцип работы не различается, хотя там у картриджей уже 72 контакта: несколько идут впрямую в гнездо снизу консоли (ни разу не использовалось ни в одной игре), плюс четыре идут на чип для защиты от пиратства.

Перейдём к практике.
Итак, вроде ничего особо сложного нет. Хотя CPU и PPU память не необходимо читать сразу, и их можно было бы подключить к одним и тем же ногам, но для первого опыта я решил их изолировать. Да, это чрезвычайно сверхизбыточно, но мне просто необходимо большущее количество ног – у картриджа их всё-таки 60. Нужно просто как-то прочесть все данные по всем адресам и сохранить их в NES-файл. Тем наиболее так еще проще разводить плату, двустороннюю делать мне совершенно не хотелось. Для этого я решил взять два микроконтроллера ATMEGA64.

Разъем для картриджей можно и приобрести, это обычный краевой разъём на 60 ног, но он почему-то везде был лишь под заказ, потому я просто выпаял его из дешевенькой новодельной денди.

Опосля сборки и печати корпуса устройство вышло таковым:

Не буду вдаваться в подробности прошивки, выше уже изложены принципы работы с памятью, а исходники будут в конце статьи.

Проще говоря, всё то, что мы привыкли созидать на сборниках типа «9999999 in 1» с циклическими играми. В таковой размер вписывались лишь самые простые игры типа «Battle City», «Ice Climber», «Duck Hunt», «Tetris», «Lode Runner». И совсем не из-за его малого размера, а из-за того, что адресное место для кода ограничивалось этими самыми $8000-$FFFF, а это всего-то 32 кб. Как досадно бы это не звучало, на самом деле нет. Жизненный срок у NES и Famicom был довольно долгим, и создатели игр чрезвычайно быстро (уже в 85м году) столкнулись с тем, что при таком подходе в картридж можно впихнуть чрезвычайно не достаточно инфы. Всё ли так просто?

Так в картриджи начали ставить мапперы.

Вы его проходите, маппер переключает банк памяти, и в итоге полностью по тому же самому адресу считывается уже код не первого, а второго уровня. Аналогично и с видеопамятью. Представьте, что по какому-то адресу хранится код первого уровня игры. Это такие микросхемы, которые отвечают за переключение банков памяти, в итоге чего же возникла возможность значительно расширить адресное место.

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

Далековато ходить не нужно: снутри фаворитных у нас пиратских многоигровых картриджей что лишь не стоит. Первопроходчикам приходилось дампить 1-ый банк памяти, дизассемблировать его и заниматься реверс-инжинирингом, чтоб осознать, как же получить доступ к оставшейся части данных. А китайцы до сих пор выпускают новейшие игры на собственном своем железе, в котором разобраться стало ещё труднее. Выходит, что на теоретическом уровне может показаться картридж, который не лишь трудно будет сдампить, но и который не будет эмулироваться ни одним имеющимся эмулятором. При этом в заголовке NES-файла указывается общепринятый номер маппера, а настоящий эмулятор должен эмулировать не лишь саму консоль, но и весь этот зоопарк железа, которое ставили в картриджи.

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

Клиентскую часть к дамперу я пишу на C#, потому просто обрисовал интерфейс IMapper и класс, который соответствует каждому мапперу: Я решил воплотить хотя бы чтение игр на самых фаворитных мапперах.

У каждого реализованы способы для дампинга данных. Вот как смотрится способ чтения программной памяти игры на MMC3 маппере:
", banks-2, banks-1);
data.AddRange(dumper.ReadPrg(0xC000, 0x4000));
Console.WriteLine("OK");
} public void DumpPrg(FamicomDumperConnection dumper, List<byte> data, int size)
{
dumper.WritePrg(0xA001, 0);
byte banks = (byte)(size / 0x2000);
for (byte bank = 0; bank < banks-2; bank += 2)
{
Console.Write("Reading PRG banks #{0} and #{1}… ", bank, bank+1);
dumper.WritePrg(0x8000, 6);
dumper.WritePrg(0x8001, bank);
dumper.WritePrg(0x8000, 7);
dumper.WritePrg(0x8001, (byte)(bank | 1));
data.AddRange(dumper.ReadPrg(0x8000, 0x4000));
Console.WriteLine("OK");
}
Console.Write("Reading PRG banks #{0} and #{1}…
Раз кому любопытно, описание этого маппера можно почитать тут: wiki.nesdev.com/w/index.php/MMC3

Я решил испытать побыть на месте первопроходцев и сдампить картридж вот с таковым необыкновенным меню:

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

Опосля этого я прочел картридж опять, предварительно выполнив запись по адресу $B600, и получил уже полностью работоспособный ROM. И даже раз я прослежу, что же происходит в момент выбора игры в меню, и прочитаю весь картридж, эмулятор быстрее всего никак не сумеет всё это запустить. Само собой, игры в нём не запускаются, ведь для этого необходимо опять переключать банки памяти.

Я попробовал её прочесть и скормить эмулятору. Ещё мне в руки попал лицензионный картридж одной из самых культовых игр тех времён — «The Legend of Zelda». Он без заморочек её сообразил. Сработало. Делать дамп данной игры смысла нет, она заинтриговала меня иным. Опосля этого я ради опыта решил прирастить в ней число сердечек и записать назад в картридж. В этом картридже стоит доборная RAM память и батарейка, что дозволяет сохраняться в игре. Он без заморочек работает и с дампером, и с Фамикомом через обычный пассивный переходник. Лежит эта память в спектре $6000-$7FFF.

Вышла смешная возможность переносить сохранения меж эмулятором и настоящей консолью.

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

Ссылки на исходники:
github.com/ClusterM/famicom-dumper — сам дампер (исходники на C, разводка платы, 3D модельки корпуса)
github.com/ClusterM/famicom-dumper-client — клиент на C# habrahabr.ru