Немного истории 4

Помните, как пираты испортили игрушку «Snake Rattle’ n Roll»? Главной проблемой модифицированного кода тогда была неочистка регистра при выходе из функции, которая должна была неявно это делать. Оказывается, пираты в свое время так и не извлекли урок из этого. Сегодня у нас на рассмотрении чуть более поздняя переделка одной известной игры, а именно:

Это всем известная версия игры «Ninja Gaiden» / «Ninja Ryukenden» для европейского рынка. В пиратском варианте в очередной раз была сделана попытка сконвертировать игру для маппера памяти ММС1 на новый маппер — ММС3. Это не всегда достаточно просто, но в целом не должно вызывать сильных затруднений. Хотя… Пиратская версия игры после подобной модификации потеряла… БОНУСЫ! А именно, висящие по стенам лампы, в которых можно найти в том числе новое оружие. В данной игре их просто нет. То есть нельзя не только пополнить жизни или число зарядов специального оружия, нельзя даже взять это специальное оружие!

Естественно, первое подозрение падает на ошибки при модификации кода под новый маппер. Процедуры смены банка разбросаны по разным частям игры, также дополнительно смена банков происходит непосредственно в отдельных частях кода, без вызова подпрограмм — так быстрее и проще. Помня о прошлых ошибках, сразу пойдем посмотрим одну из функций смены банка в пиратской версии:

	$C02A 0A         ASL     A
	$C02B 48         PHA
	$C02C A9 06      LDA     #6
	$C02E 8D 00 80   STA     $8000
	$C031 68         PLA
	$C032 8D 01 80   STA     $8001
	$C035 09 01      ORA     #1
	$C037 48         PHA
	$C038 A9 07      LDA     #7
	$C03A 8D 00 80   STA     $8000
	$C03D 68         PLA
	$C03E 8D 01 80   STA     $8001
	$C041 A9 00      LDA     #0
	$C043 60         RTS

Невероятно, она чистит явно регистр А при выходе! Значит извлекают-таки урок пираты из прошлых ошибок! Значит проблема где-то еще. Смотрим, откуда вызывается данная функция:

	$D8D5 A2 00      LDX     #0
	$D8D7 8A         TXA
	$D8D8 20 2A C0   JSR     $C02A
	$D8DB EA         NOP
	$D8DC EA         NOP
	$D8DD EA         NOP
	$D8DE EA         NOP
	$D8DF EA         NOP
	$D8E0 EA         NOP
	$D8E1 EA         NOP
	$D8E2 EA         NOP
	$D8E3 EA         NOP
	$D8E4 EA         NOP
	$D8E5 EA         NOP
	$D8E6 EA         NOP
	$D8E7 EA         NOP

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

	$D8D5 A2 00      LDX     #0
	$D8D7 8E FF FF   STX     $FFFF
	$D8DA E8         INX
	$D8DB 8E FF FF   STX     $FFFF
	$D8DE CA         DEX
	$D8DF 8E FF FF   STX     $FFFF
	$D8E2 8E FF FF   STX     $FFFF
	$D8E5 8E FF FF   STX     $FFFF

Но ПОСТОЙТЕ! Маппер ММС1 имеет 5-битный регистр для программного ПЗУ, а записывается этот регистр последовательно по одному биту, так что надо записать в регистр пять раз подряд биты требуемого числа. В коде выше это как раз и происходит. Что мы видим в этом коде. Первый записанный бит — это 0. Потом опкод INX увеличивает содержимое регистра Х на 1 и таким образом второй записанный в регистр бит — это 1! Далее идет DEX, таким образом три следующих бита снова будут 0. Число, записанное в регистр и номер банка в двоичной форме: 00010, то есть в десятичном виде — 2! А почему тогда пиратский код передает в функцию смены банка 0? Потому что кто-то не увидел разницы между универсальной функцией смены банка, в которую передается одно число и биты его сами сдвигаются при записи, и записью напрямую требуемых битов, без сдвигов и прочего!

Чтобы исправить игру, достаточно заменить «LDX #0» на «LDX #2»! И все. Но останется ли что-то интересное тогда в игре для собирателей пиратки? 😉

Немного истории 3

Это настолько банально, что даже оказалось неинтересно. Очередная причина бешенства и самоубийств среди школьников: пиратский FrankenStein. Опознать поцЫента можно по отсутствующей букве «N» в текстах по всей игре и искореженному логотипу «BANDAI».

 

Проблема, собственно, проста. Во втором уровне, при попытке прыгнуть на «островок», игра намертво зависала. Вот это место:

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

	$A23C 85 18      STA     $18
	$A23E 20 B7 81   JSR     $C1B7         ; BAD 81 -> 80
	$A241 BD 04 06   LDA     $604,X
		...
	$B13E 29 0F      AND     #$F
	$B140 C9 01      CMP     #1            ; BAD C9 -> C8 (INX)
	$B142 F0 11      BEQ     $B155
		...	
	$B73E 30 04      BMI     $B744
	$B740 A9 FF      LDA     #$FF          ; BAD A9 -> A8 (INY)
	$B742 D0 02      BNE     $B746

В трех байтах пропал самый младший бит. Просто банально не считался. Все три бита попадают в область исполняемого кода, все три теоретически могли бы стать причиной сбоя, так как два из трех получившихся после сбоя опкодов имеют длину в 1 байт, вместо 2, а в одном опкоде изменился адрес перехода на подпрограмму. По стечению обстоятельств, он и стал причиной сбоя. Опкод находится в функции обработки приземления на «островок» и вызывает переход на BAD опкод, соответственно срабатывание IRQ прерывания и выход в бесконечный цикл системных прерываний по сбою.

Немного истории 2

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

Несмотря на вполне определенное название «Robo Cop III» на картридже игра не имела ничего общего со всем известным Роботом-полицейским и играми по одноименному фильму. Название игры на титульном экране, как и положено в любой нормальной японской игре, написано по-японски, но, хотя и существует другая пиратская версия этой игры с другим, не менее распространенным для нее названием «Super Resque» на титуле, было прекрасно видно, что игру «Tokkyuu Shirei — Solbrain» с натяжкой можно все-таки назвать игрой про робота-полицейского.

Что же получали счастливые обладатели сего пиратского картриджа? Начнем с того, что оригинальная версия игры имела возможность выбора любого из оставшихся пяти уровней в произвольном порядке после прохождения первого обязательного. В пиратской же версии того, кому удалось пройти первый уровень, выбрасывало сразу же на уровень F с горящими зданиями и большим лифтом, поднявшись на котором, игрок попадал к боссу.

Тут то и ждало главное разочарование. Убив босса, игрок не перемещался на следующий уровень или в режим их выбора. Вместо этого босс уровня появлялся снова живее всех живых и битва продолжалась. Самые упорные убивали его несколько десятков раз подряд, так и не достигнув никакого результата. Прочие оставляли картридж пылиться на полке.

Так в чем же проблема? У вас появилась эксклюзивная возможность узнать это прямо сейчас. 😉 Начнем с того, что пиратская игра отличается от оригинальной не только стертыми копирайтами на титульном экране, что можно наблюдать на скриншотах выше, но и наличием встроенного искусственно чита для восстановления жизней. В любой момент игры можно, нажав знакомую до боли комбинацию Start + Up, восполнить потерянную энергию. Вот он, код чита, добавленный в конец файла:

	$FF80 26 91      ROL     $91
	$FF82 A5 90      LDA     $90
	$FF84 29 18      AND     #$18
	$FF86 C9 18      CMP     #$18
	$FF88 D0 05      BNE     $FF8F
	$FF8A A9 08      LDA     #8
	$FF8C 8D C5 05   STA     $5C5
	$FF8F 60         RTS

Виноват чит? Ни в коем случае. Он абсолютно ничему не мешает. Помешала как всегда спешка. Во время хака, судя по всему, было принято решение изменить игру таким образом, чтобы она отличалась от оригинала. Как правило, проще всего это делается изменением порядка уровней. Было принято решение убрать выбор уровня и сделать игру линейной, с чередующимися друг за другом уровнями и боссами. Так как внутренние номера уровней и боссов не расположены в строгом порядке возрастания, а в общем то выбираются по совершенно другим соображениям, предполагалось, что не только порядок уровней, но и соответствие боссов уровням будет иное. Таким образом китайскими умельцами была сделана простейшая модификация кода:

	$DAF2 A5 55      LDA     $55
	$DAF4 EA         NOP                   ; BNE     $DAFB
	$DAF5 EA         NOP                   ;
	$DAF6 A9 1D      LDA     #$1D
	$DAF8 85 02      STA     2
	$DAFA 60         RTS
	$DAFB 20 C9 C5   JSR     $C5C9
	$DAFE A9 13      LDA     #$13
	$DB00 20 8C EF   JSR     $EF8C

Опкод BNE, проверявший, пройден ли первый обязательный уровень и перенаправлявший в положительном случае управление на код выбора дальнейшего уровня, стерт начисто. Теперь игра должна была бы получать сразу номер уровня в переменной $55 и двигаться дальше. Не тут то было.

Логикой игры не предусмотрено последовательное увеличение номера уровня. Соответственно, первый уровень имеет индекс 0 и инициализируется при старте. После прохождения первого уровня, переменной номера уровня явно присваивается первое и последнее ненулевое значение — 1, которое является индексом уровня F, стартующего сразу за пройденным уровнем A. В дальнейшем все изменения переменной номера уровня осуществляются только в режиме выбора уровня и в конце каждого из них при переходе к боссу. Сами того не понимая, пираты просто напросто отключили любую возможность перехода в другой этап, отключив ветку кода с режимом их выбора. Результат мы можем ощутить воочию.

Подобные вещи не столь редкое явление для пиратских картриджей. Отдельной темы заслуживает система противопиратской защиты в играх Konami, ставшая причиной огромного числа проблем у пользователей пиратской продукции у нас в стране. Неубиваемые Шреддеры, легко убиваемый Bucky O’Hare, зацикливание уровней — только малая часть всего веселья, о котором я, может быть, расскажу в следующий раз.

Если вы знаете еще какие-то примеры подобного рода «глюков» в пиратских играх, рассказывайте. Может быть кто-то знает и имеет пиратский картридж с данной игрой, лишенной описанного недостатка. Кто знает, может быть и они смогут найти свое логическое объяснение.

Love Story

Практически каждая третья игра для Денди кроме основной игровой программы содержит что-либо, не относящееся так или иначе к игре. Это так называемые пасхальные яйца или скрытые послания, возможно даже не обнаруживаемые никак иначе, кроме как просмотром дампа игрового ПЗУ. Причины, побуждающие авторов игр оставлять закладки в своих творенияx, могут быть разные: от желания обладать неким секретом, известным только тебе, до, возможно, романтики посланий в будущее, вроде бутылок, брошенных в море, или капсул, закопанных в землю. Тематика также разнообразна. Конечно же основная масса пасхалок — копирайты авторов в той или иной форме: фамилия разработчика в фукнции проверки на теплый резет, полный список разработчиков, жалоба на начальника, рассказ о себе или о своих увлечениях. Но что интересно, второй по популярности темой скрытых посланий являются признания в любви! Кто бы мог подумать, что таким способом можно кому-то признаваться в своих чувствах. Тем не менее…

Все вы, наверное, в детстве играли в игру «Battle City». Сложно было оторваться, даже если не получалось пройти десятый уровень, что-то заставляло снова и снова включать приставку и играть. Даже если она была выключена, стоило лишь только закрыть глаза, снова был слышен шум моторов ползающих по полю танков. Но была еще одна вещь, о которой никто не знал ни в момент выхода игры в 1985-ом, ни позднее. Если на титульном меню поставить указатель на опцию «CONSTRUCTION», нажать 14 раз кнопку Start на первом джойстике, таким образом войдя и выйдя из режима создания карт 7 раз, затем при зажатой на первом джойстике кнопке Down нажать кнопку А на втором джойстике 8 раз, а при зажатой на первом джойстике кнопке Right нажать на втором джойстике 12 раз кнопку В и в конце концов нажать на первом джойстике Start, то строчка за строчкой медленно появится сообщение, а затем по экрану упадет «слезинка»…

Кто же этот «OPEN-RICH», так неравнодушный к некой Норико? Имена авторов игры на нашу удачу прописаны внутри игрового ПЗУ в виде скрытого текста, нигде в игре не отображающегося. Это «RYOUITI OOKUBO», «TAKEFUMI HYOUDOU» и «JUNKO OZAWA». Последнего отбрасываем сразу, это музыкант, его имя и сейчас можно найти в титрах некоторых современных игр, например «London — Hellgate». Второй в общем тоже является старым сотрудником NAMCO, его имя тоже довольно часто встречается в титрах довольно большого количеств игру, но в качестве кого угодно, кроме программиста. Стало быть, Рьёити Оокубо как раз программист, и он и есть наш «OPEN-RICH».

Занимательно то, что он не работал в NAMCO, а являлся владельцем небольшой компании по разработке программного обеспечения для игровых консолей и различных устройств, которая непосредственно делала игру «Battle City». Компания называется «TomCat Systems» и существует до сих пор. В игровом ПЗУ также содержится намек и на это: в конце ПЗУ с программой имеется небольшой кусочек своего рода ASCII картинки, на которой изображены шесть иероглифов сгруппированных тройками: два маленьких вверху, один большой — под ними.

Первые три иероглифа — ни что иное, как фамилия программиста — «Ookubo». Оставшиеся три образуют еще одно имя собственное: «Utakawa». Так называется улица в Токио в районе Сибуйя, в далеком 1985-ом году именно здесь располагалась штаб-квартира «TomCat Systems», но позднее, в 1997 году был открыт развлекательный комплекс «NAMCO INTI SHIBUYA».

В следующий раз псевдоним владельца этой фирмы появляется только в 1991-ом году в варианте игры «Castle Quest» для Famicom, но в сокращенном варианте: «OPR». Опять же, в роме присутствует и фамилия автора скрытым текстом, хотя немного в другой романизации. Самое забавное тут в том, что среди графических дизайнеров фирмы NAMCO одно время числилась некая Норико Икегава. Если это и есть наша Норико, полагаю, что она в полной мере могла оценить данное послание.

Издателем вышеупомянутого «Castle Quest» была фирма HUDSON. С ней связана отдельная любовная история, растянувшаяся на как минимум пару лет и сразу несколько игр. Программист Кикута Масааки (菊田政昭), работавший в HUDSON в середине 80-х, был поклонником «Звездных Войн» и местных «Raiders». Но больше всего на свете он любил оставлять в своих играх пасхальные яйца. Практически в каждой игре, к которой он приложил руку, встречается какой-нибудь секретный код или остатки от него — от простых сокращений своего имени, до графического изображения самого себя. Первая задокументированная попытка вставки чита в игру была предпринята еще в 1984-ом году в игре «Raid on Bungeling Bay» и представляла собой код для вывода на экран фразы «BY KIKU» под строкой с копирайтами, но в последствии он был по тем или иным причинам отключен.

Чуть позднее, в 1985-ом, в его игре Binary Land два пингвинчика мужского и женского пола на многочисленных уровнях пытаются найти друг друга. Как подобает настоящим влюбленным, они действуют и чувствуют одинаково даже на расстоянии. В стандартной вариации игры имена пингвинчиков — «GURIN» и «MALON». Но если зажать на обоих джойстиках А + В и нажать Reset, то имена на титульном экране сменятся на «KIKU» и «MEGU», а строка с копирайтами превратится в «LOVE STORY».

С этого момента, пингвинчики во время прохождения смогут не только синхронно двигаться, но и звать друг-друга по имени, когда кто-то из них попадет в беду! Конечно, «KIKU» есть ни кто иной, как господин Масааки. Но кто такая «MEGU»?

В этом же году выходит новая игра Кикуты на ту же любовную тему, но теперь герою предстояло спасти свою возлюбленную из лап злодея. Подруга героя сделала все возможное, чтобы тот ее непременно отыскал, разбросав по пути множество личных предметов. Игра называлась «Challenger» или по-нашему «Претендент». Казалось бы, что здесь необычного… Ничего, кроме невероятно изобретательного чита.

В игре перед показом титульного экрана выводится сообщение «REALTIME ACTION ADVENTURE!». Если же вы смогли дойти до второго уровня и во время путешествия по миру нажимали кнопки А и В в следующей последовательности: A, B, A, A, B, B, A, A, A, B, A, A, B, B, B, B, A, B, A, B, A, B, B, A, A, B, A, A, A, B, A, B — то   появится сходная надпись «KIKU MEGU LOVE STORY!».

Самое интересное тут то, что если взять английское слово «LOVE», затем записать ASCII коды его символов в двоичном формате — «01001100 01001111 01010110 01000101», то получим ни что иное, как нашу читовую последовательность, где кнопке А соответствует «0», а кнопке В — «1». Вот такая вот программистская изобретательная любовь.

Независимо от сюжета и направленности игры Кикута не изменяет своему обычаю оставлять секреты. В следующем 1986-ом году выходит космическая леталка «Star Soldier». Читы в игре присутствуют, но касаются преимущественно модификации игрового процесса. В игре представлены два набора графики для уровней и врагов. В обычном режиме второй набор включается после первого прохождения игры. С помощью чита можно получить результат сразу, не тратя время на прохождение. Игровой процесс в целом не меняется, возможно лишь увеличивается общая сложность. Но вот в самом графическом ПЗУ есть нечто занимательное. В каждом банке графики спрятана небольшая строчка текста на 16 символов. Своя на каждый из блоков. И вот что интересно, блоку графики достаточно мирного содержания, с бабочками, паучками и прочими креветочками соответствует строчка «I love M.Akakura». Второму же, где уже присутствуют Чужие, черепушки и смерти с косами соответствует строчка «Madwoman Megumi».

Появляется подозрение, а не одна ли и та же эта женщина во всех случаях? «MEGU» вполне может быть сокращением от «Megumi», как и «М» с фамилией «Akakura». Можно предположить, что Мегуми Акакура тоже была программистом на фирме HUDSON и просто не воспринимала больше никаких любовных посланий, кроме подобных, понятных и интересных только программистам. Факт того, что она присутствует в двух ипостасях: любимой и злобной женщины может являться подтверждением долгих серьезных взаимоотношений между людьми, рано или поздно увлекающихся ссорами. Хотя, кто знает. Может быть это две разных женщины: первая — новая возлюбленная Кикуты, а вторая — та самая Мегу из прежних лет. Тем более, имя «Мегуми» достаточно распространенное.

В 1991-ом году вышел симулятор рыбной ловли «Blue Marlin, The» от компании HOT-B. Игра достаточно редкая в наших краях, и скорее всего была японская, так что мало кто мог в нее достойно поиграть. Это не так страшно, потому что строчка текста «MEGUMI.MY.LOVE» все равно никогда не появляется на экране ни при каких обстоятельствах. Найти ее можно только в дампе ОЗУ игры после запуска или в ПЗУ картриджа. Используется же она для проверки при старте на предмет того, была ли приставка перезагружена или включена кнопкой Power. Так как разработчик игры не указан, нельзя сказать с уверенностью, та ли эта «Мегуми», что и у Кикуты Масаки или нет. Скорее всего нет. Но вот что характерно, для такого приема проверки на первый запуск почти все програмисты, за исключением конкретно этого, используют свою фамилию. Не означает ли это, что бОльшая часть программистов любит прежде всего, все-таки, себя? 😉

Вышеперечисленные примеры относятся к своего рода сверхсекретным читам, они по сути своей не предназначены для случайного нахождения и должны выполняться самим программистом в присутствии объекта обожания, причем скорее всего тоже причастного к разработке игры. Это можно назвать сюрпризом, но довольно специфическим. А как на счет возлюбленной, не имеющей отношения к игропроизводству, но тем не менее любящей игры? Тут программисты тоже не подкачали, благо число способов, которыми можно закодировать секретное послание огромно. В игре «King of Kings» от той же NAMCO двумя годами позже, в 1988-ом, появилась возможность записывать игровое состояние, вводя при этом свое имя. Если выбрать четвертый слот для сохранения и ввести в качестве имени «オオタニ トモコ» (Оотани Томоко) или «アヘ゛ ヒロミ» (Абэ Хироми) и перед подтверждением ввода зажать на втором джойстике кнопку А или В, или обе сразу, то в списке сохраненных игр четвертый слот станет называется «ILOVEYOU». Неплохо, не правда ли?

Кого еще должен любить чертпрограммист? Естественно он должен любить маму с папой и прочих окружающих, если он, конечно, не злобный гений, вынашивающий планы по захвату мира. В 1989-ом году вышла игра, являющаяся замечательным тому подтверждением. В «Jesus — Kyoufu no Bio Monster» кроме обычных присутствуют еще и «совершенно секретные» титры, которые можно активизировать специальным секретным паролем «ふあみこん せいさくの なかまたち みせてくれ» (в игре есть еще три секретных пароля, но о них здесь говорить не будем). В этих титрах, кроме раздачи всем участникам процесса забавных прозвищ, передаются приветы по случаю. И тут собраны сразу все варианты: два из четырех «приветов» — признания в любви к женщинам, одно — признание в любви матери, одно — кровати «за поддержку», и в довершение — благодарность человеку, работавшему в качестве таксиста. 😉

Картина была бы неполной, если бы не был упомянут еще один вариант признаний в любви: дразнилки. «Тили тили тесто…» — классика жанра. Как ни странно, такой формат программистам тоже совершенно не чужд. Игра по мотивам одноименного «стебного» ужастика «Attack of the Killer Tomatoes», снятого аж в 1976 году, была выпущена спустя 15 лет, в 1991-ом. Не очень распространенная в нашей стране игра, которую, тем не менее, многие видели и на пиратских картриджах. К довольно милому набору шуточек самой игры добавилась одна — секретная. После смерти героя на экране на некоторое время появляются глаза хищного помидора-убийцы, а затем на закидываемом помидорами экране проступают буквы:

Если же сразу после смерти, до того как на экране появятся глаза, зажать на первом джойстике A + B + Left + Down, то вместо вышеуказанной надписи появится другая:

Программиста этой игры, как ни странно, зовут Rob Harris, но из многочисленных проектов с его участием в начале 90-ых, ни в одном нет подобного пасхального яйца или даже чит кода или секрета. Надпись в таком случае похожа на классическую «обзывалку», неужели кто-то пошутил над Робом, спрятав такую «обзывалку» в его же собственную игру? Или Роб любит говорить о себе в третьем лице? Кто знает. Можно попробовать спросить самого Роба. 😉

Немного истории

Давным-давно, в далекой-предалекой галактике… В общем была такая приставка «Денди». И было на нее море пиратских картриджей разной степени паршивости. Но один не давал мне покоя.

Через некоторое время после покупки желанной «Денди», я, как и все, столкнулся с проблемой, что комплектный картридж с «танчиками» уже не вставляет, как раньше. Назрела необходимость покупки нового, чтобы было хотя бы на что меняться с другими товарищами. Продавались картриджи с помпой по всей Москве, так что был выбран самый простой и удобный вариант — Универмаг Московский возле Ярославского вокзала. На третьем этаже был целый отдел с игрушками, причем не только от фирмы Steepler.

К выбору картриджа я подошел со всей тщательностью. Выбирал самый тяжелый и самый желтый картридж из всех. Чем тяжелее картридж, тем больше в него влезало игр, причем наклейка на этот показатель никак не влияла. Забегая вперед, скажу, что на моем в последствии наклейки не оказалось вовсе. Наконец, выбор был сделан: многоигровка 7-в-1 с «Черным плащом», «змейками» и «Magic Jewel».

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

Была лишь одна вещь, которая омрачала мою радость. Игра «Snake Rattle’n Roll» на этом картридже всегда намертво зависала в седьмом уровне на «фонтанчиках». Как только брался приз-часы, появлялся одинокий фонтанчик, а дальше следовала неминуемая смерть. Обидно было еще и потому, что даже с секретными переходами между уровнями, известными мне на тот момент, добраться до того места было ой как сложно. Впоследствии был, конечно же, найден переход из первого уровня сразу в восьмой, что несколько спасло дело, но это было уже достаточно поздно и всерьез игру пройти так и не удалось. Спустя время, картридж и вовсе затерялся в чьих-то закромах и так ко мне и не вернулся. Но история на этом не заканчивается, а начинается новая.

Некоторое время назад я получил по почте пачку картриджей (кстати, огромная просьба тем, кто мне что-то отправлял вне зависимости, хотел бы он вернуть картриджи обратно или нет, свяжитесь со мной по почте, и засвидетельствуйте принадлежность тех или иных посылок именно вам; память у меня плохая, а посылки скопились за довольно большое время, сам я уже могу и не вспомнить, кого за что благодарить). Среди тех картриджей оказался один, полу-живой, с явными следами вандало-ремонта, и показывал он только одну игру «Черный плащ» с глюками в графике. На плате значилось 12-в-1. После выпайки и считывания ПЗУ, оказалось, что это ни что иное, как тот самый родной картридж со «змейками». Вот он, герой рассказа, в коробке с оригинальной наклейкой:

7 в 1 "со змейками"

7 в 1 "со змейками"

Замулить маппер не составило большого труда, благо все игры имеются в отдельном виде, все (кроме простых) работают на оригинальной версии MMC1 от Nintendo и соответственно могут быть выделены нужные для переключения банки ПЗУ и для пиратского маппера. К слову сказать, несмотря на простоту, пиратский вариант сам по себе позволяет практически идеально имитировать работу большинства картриджей на базе MMC1, с тем лишь исключением, что каждую игру необходимо индивидуально модифицировать. В частности, «Робокоп» требовал от пиратов огромных усилий по модификации кода, потому как функции смены банков разбросаны в нем по всему ПЗУ, с чем доблестные тайваньцы успешно справились.

Пришла очередь узнать наконец, что же было причиной такого жестокого зависания «змеек».

То самое место

То самое место

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

Оригинальный код

	$FFC9 8D FF BF   STA     $BFFF
	$FFCC 4A         LSR
	$FFCD 8D FF BF   STA     $BFFF
	$FFD0 4A         LSR
	$FFD1 8D FF BF   STA     $BFFF
	$FFD4 4A         LSR
	$FFD5 8D FF BF   STA     $BFFF
	$FFD8 4A         LSR
	$FFD9 8D FF BF   STA     $BFFF
	$FFDC 60         RTS

Пиратский модифицированный код

	$FFC9 0A         ASL
	$FFCA 0A         ASL
	$FFCB 0A         ASL
	$FFCC 8D FF BF   STA     $BFFF
	$FFCF EA         NOP
	$FFD0 EA         NOP
	$FFD1 2C FF BF   BIT     $BFFF
	$FFD4 EA         NOP             
	$FFD5 2C FF BF   BIT     $BFFF
	$FFD8 4A         LSR
	$FFD9 2C FF BF   BIT     $BFFF
	$FFDC 60         RTS     

Часть своих данных игра хранит видео-ПЗУ и периодически ей надо их оттуда считывать. В частности, в видео-ПЗУ хранятся некоторые данные уровня, например, как раз данные активных объектов. Когда мы берем приз-часы, игровое поле модифицируется так, чтобы появились анимированные фонтанчики. Сначала вызывается функция смены банка видео-ПЗУ, а потом функция копирования данных из видео-ПЗУ в ОЗУ приставки. Обе функции вызываются одна за другой, первая с параметром номера банка в регистре А, вторая с параметром индекса массива данных, с которого необходимо начать копирование данных.

Не так явно бросается в глаза тот факт, что оригинальная функция смены банков всегда очищает регистр А, так как сдвигает его на 5 бит вправо, а число, в него записываемое, не превышает 31. Таким образом, в оригинальной игре, после вызова функции смены банка, функция копирования всегда должна инициализироваться нулевым значением в регистре А. У пиратов же, функция состоит из старой части, в которой опкоды MOV заменены на BIT, чтобы не влиять на пиратский маппер, а нужное значение для его собственного регистра получается сдвигом индекса банка графики на 3 влево. Таким образом, на выходе из этой функции регистр А не очищается, а его значение передается как индекс начала области данных для копирования. В определенный момент нужные данные пишутся не туда и приводят к фатальному сбою.

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

Вот и сказочки конец, а кто дочитал до этого места, получит приз.

Some notes about Naomi protected ROM-board

 Несколько слов о защитной схеме картриджей аркадной системы Naomi1/Naomi2.

 Схема защиты является надстройкой над стандартным режимом чтения данных ROM-платы
 управляется через одни и те же системные регистры: 0x5f7000, 0x5f7004, 0x5f7008 для
 PIO режима и 0x5f700с, 0x5f7010 для DMA режима.
 Непосредственно схема защиты осуществляет расшифрование предварительно зашифрованных
 секретных данных с заданным ключом и, возможно, зашитым внутри уникальным ключом.
 Частично ключ передается через специальный внутренний регистр схемы защиты, частично
 вместе с зашифрованными данным. Последнее слово зашифрованных данных также представляет
 собой служебное значение, предположительно признак конца зашифрованных данных.
 Также схема защиты может осуществлять перестановку битов заданного смещения, для
 получения нового значащего адреса начала данных в ROM-плате.

 Выбор между управлением функциями ROM-платы и схемы защиты осуществляется путем установки
 специальных флагов регистра 0x5f7000.

 NAOMI_ROM_OFFSETH		                          Адрес регистра: 0x5f7000
 NAOMI_DMA_OFFSETH		                          Адрес регистра: 0x5f700c
 +----+----+----+-----+----------------------------------------------------------+
 | 15 | 14 | 13 |  12 |                       bit11-0                            |
 +---------+----+-----+----------------------------------------------------------+
 | AA | SE | SC | res |              ROM board offset hi bits                    |
 +---------+----+-----+----------------------------------------------------------+

  Используется для установки старшей части смещения данных ROM-платы для чтения в
  PIO режиме или старшей части адреса внутреннего регистра схемы защиты в зависимости
  от установленных флагов:
 
        +-----+----------------------------------------------------+ 
        |Flags| Description                                        |
        +-----+----------------------------------------------------+ 
	| AA  | Автоинкремент смещения при операциях чтения или    |
        |     | записи на ROM-плате.                               |
        +-----+----------------------------------------------------+
	| SE  | Выбор адресного пространства схемы защиты          |
        +-----+----------------------------------------------------+
	| SC  | Режим перемешивания битов, если установлен, адреса |
        |     | работают в нормальном режиме. Если очищен, любое   |
        |     | смещение при доступе к данным подвергается         |
        |     | перестановке битов.                                |
        +-----+----------------------------------------------------+


 NAOMI_ROM_OFFSETL		                          Адрес регистра: 0x5f7004
 NAOMI_DMA_OFFSETL		                          Адрес регистра: 0x5f7010
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                        ROM board offset low word                              |
 +-------------------------------------------------------------------------------+

  Используется для установки младшей части смещения данных ROM-платы для чтения в
  PIO режиме или младшей части адреса внутреннего регистра схемы защиты в зависимости
  от установленных флагов. Как правило устанавливается первым.
                 

 NAOMI_ROM_DATA			                          Адрес регистра: 0x5f7008
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                                 Data word                                     |
 +-------------------------------------------------------------------------------+

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

 - Непосредственное чтение данных ROMа через схему защиты с осуществлением
   декодирования на лету. В данном режиме схеме защиты доступны только первые 16
   мегабайт пространства ROM-платы, содержащие программный код. Вне этого объема
   непосредственное чтение невозможно. Используется достаточно редко (Dead or
   Alive 2, Dead or Alive 2 Millenium, Guilty Gear X).
 
 - Передача во внутренний буфер схемы защиты объемом 64 килобайта набора данных для
   расшифрования с последующим считыванием в расшифрованном виде. Так как
   используется внутренний буфер, место хранения зашифрованных данных не имеет
   значения. Блок может считываться из ROM-платы обычным способом в промежуточный
   буфер, а потом передаваться на схему защиты. Наиболее распространенная схема.

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

 PROT_ROM_OFFSETL		                                Offset: 0x4001fff8
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                      Prot ROM board word offset low word                      |
 +-------------------------------------------------------------------------------+
 
 PROT_ROM_OFFSETH		                                Offset: 0x4001fffa
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                      Prot ROM board word offset hi word                       |
 +-------------------------------------------------------------------------------+

  В режиме прямого расшифрования получают смещение зашифрованных данных в ROM-плате,
  заданное индексом первого 16-битного слова, другими словами - это значение смещения
  данных в ROM-плате в байтах, сдвинутое на 1 бит вправо.
  
  В режиме расшифрования данных через внутренний буфер схемы защиты (максимальный
  размер данных - 64 килобайта), в эти регистры записывается специальное смещение,
  переключающее ROM-плату на чтение из внутреннего буфера: 0х01000000.
  
 PROT_ROM_KEY		                                        Offset: 0x4001fffc
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                                Key data bits                                  |
 +-------------------------------------------------------------------------------+

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

 PROT_ROM_DATA		                                        Offset: 0x4001fffe
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                            Unprotected data word                              |
 +-------------------------------------------------------------------------------+

  Установка данного смещения переключает схему шифрования в режим расшифрования
  и чтения открытых данных. Считывание данных производится путем чтения из регистра
  NAOMI_ROM_DATA, формат выходных данных - big-endian.

 PROT_ROM_DATA		                                        Offset: 0xс2020000
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                             Protected data word                               |
 +-------------------------------------------------------------------------------+

  Установка данного смещения переключает схему шифрования в режим записи зашифрованных
  данных во внутренний буфер схемы защиты. Данные записываются последовательно через
  порт NAOMI_ROM_DATA ROM-платы, указатель данных фнутреннего буфера увеличивается
  после каждой записи.


 Примеры различных вариантов схемы декодирования

 
 Раскодирование данных через внутренний буфер:
 
 Write: 0x005f7004, 0x0000	; младшее слово специального смещения внутреннего
                                ; буфера на запись (0хс2020000)
 Write: 0x005f7000, 0xc202	; старшее слово специального смещения внутреннего
                                ; буфера на запись (0хс2020000)
                                ; выбран внутренний буфер для записи данных
 Write: 0x005f7008, 0xKKKK	; первое 16-битное слово являются служебным
				; возможно вторая часть 32-битного ключа
 Write: 0x005f7008, 0xNNNN	; запись N слов зашифрованных данных, внутренний
 	.			; указатель буфера увеличивается автоматически
 	.			; после каждой записи
 Write: 0x005f7008, 0xKKKK	; последнее слово зашифрованных данных, таким образом
				; размер передаваемых данных равен N+2 слов
 
 Write: 0x005f7004, 0xfff8	; младшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETL
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETL
 Write: 0x005f7008, 0x0000	; младшее слово специального смещения внутреннего
                                ; буфера на чтение (0х01000000)
 Write: 0x005f7004, 0xfffa      ; младшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETH
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETH
 Write: 0x005f7008, 0x0100	; старшее слово специального смещения внутреннего
                                ; буфера на чтение (0х01000000)
 Write: 0x005f7004, 0xfffc	; младшее слово специального смещения внутреннего
 				; регистра PROT_ROM_KEY
 Write: 0x005f7000, 0x4001	; младшее слово специального смещения внутреннего
 				; регистра PROT_ROM_KEY
 Write: 0x005f7008, 0xKKKK	; ключ расшифрования
 Write: 0x005f7004, 0xfffe	; младшее слово специального смещения внутреннего
 				; регистра PROT_ROM_DATA
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
 				; регистра PROT_ROM_DATA
 				; схема защиты в режиме раскодирования
 Read:  0x005f7008, 0xNNNN	; последовательное чтение N слов расшифрованных данных,
  	.			; внутренний указатель буфера увеличивается автоматически
   	.			; после каждого чтения
  				
 Раскодирование данных непосредственно из ROM-платы:
 
 
 Write: 0x005f7004, 0xfff8	; младшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETL
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETL
 Write: 0x005f7008, 0xLLLL	; младшее слово смещения данных в ROMе
  				; сдвинутого на один бит вправо
 Write: 0x005f7004, 0xfffa      ; младшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETH
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
                                ; регистра PROT_ROM_OFFSETH
 Write: 0x005f7008, 0xHHHH	; старшее слово смещения данных в ROMе
  				; сдвинутого на один бит вправо
 Write: 0x005f7004, 0xfffc	; младшее слово специального смещения внутреннего
 				; регистра PROT_ROM_KEY
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
 				; регистра PROT_ROM_KEY
 Write: 0x005f7008, 0xKKKK	; ключ расшифрования
 Write: 0x005f7004, 0xfffe	; младшее слово специального смещения внутреннего
 				; регистра PROT_ROM_DATA
 Write: 0x005f7000, 0x4001	; старшее слово специального смещения внутреннего
 				; регистра PROT_ROM_DATA
 				; схема защиты в режиме раскодирования
 Read:  0x005f7008, 0xNNNN	; последовательное чтение N слов расшифрованных данных,
  	.			; внутренний указатель буфера увеличивается автоматически
  	.			; после каждого чтения

Some Atomiswave info

 
 Some notes about Atomiswave carts data reading

 Even if Atomisvawe itself is mostly the same as Naomi, cartridge subsystem is
 different from Naomi/Naomi2 rom-board system.

 First of all, there is no (or rather totally unused) PIO data reading. All
 data comes throught DMA only.

 Another one, there is two independent data areas, for main programm code and
 for all other game data. First area contains in EPR-ROM chip (8Mb), second
 one contains in MPR-ROM chips (up to 128Mb). Each one area uses it's own
 offset registers and all offsets are relative from start of this area.

 Last one, game data area itself consists from two sub-areas: linear file system
 (array of 64-byte records with file names, file offsets and other information)
 and file data sub-area. Even there is one physical MPR-ROM data area, there
 is two different offset registers to access each sub-areaseparately. Each register
 is specific to it's own sub-area.
 
 All these offsets registers manage only one main internal offset register to
 work with one generic DMA. Each register access changes internal DMA offset
 according to it's behavior.

 All offsets used in these registers (except special indexes), are indexes of first
 word of requested data. Actual offset in bytes just shifted right to one bit.
 
 AW_EPR_OFFSETL                                          Register addres: 0x5f7000
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                         EPR data offset low word                              |
 +-------------------------------------------------------------------------------+

 AW_EPR_OFFSETH                                          Register addres: 0x5f7004
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                          EPR data offset hi word                              |
 +-------------------------------------------------------------------------------+

  Both low and high words of 32-bit offset from start of EPR-ROM area. Used for
  reading header and programm code data, cannot be used for reading MPR-ROMs data.
  
 AW_MPR_RECORD_INDEX                                     Register addres: 0x5f700c
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                          File system record index                             |
 +-------------------------------------------------------------------------------+
  
  This register contains index of MPR-ROM file system record (64-bytes in size) to
  read throught DMA. Internal DMA offset register is assigned as AW_MPR_RECORD_INDEX<<6
  from start of MPR-ROM area. Size of DMA transaction not limited, it is possible
  to read any number of records or just part of it.
  
 AW_MPR_FIRST_FILE_INDEX                                 Register addres: 0x5f7010
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                           First file record index                             |
 +-------------------------------------------------------------------------------+
  
  This register assign for internal cart circuit index of record in MPR-ROM file
  system sub-area that contain information about first file of MPR-ROM files
  sub-area. Internal circuit using this record to read absolute first file offset
  from start of MPR-ROM area and calculate normal offset for each other file
  requested, since MPR-ROM file data sub-area can be assighed only with relative
  offsets from start of such sub-area.
  
 AW_MPR_FILE_OFFSETL                                     Register addres: 0x5f7014
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                         MPR file offset low word                              |
 +-------------------------------------------------------------------------------+

 AW_MPR_FILE_OFFSETH                                     Register addres: 0x5f7018
 +-------------------------------------------------------------------------------+
 |                                  bit15-0                                      |
 +-------------------------------------------------------------------------------+
 |                          MPR file offset hi word                              |
 +-------------------------------------------------------------------------------+
  
  Both low and high words of 32-bit relative offset from start of MPR-ROM files
  sub-area. Used by internal circuit to calculate absolute offset using data
  from AW_MPR_FIRST_FILE_INDEX register. Cannot be used for reading EPR-ROM
  data nor even MPR-ROM file system sub-area data.

 
 In short:
 

     EPR-ROM 
 +--------------+ 0x00000000
 |              |
 |    HEADER    +- AW_EPR_OFFSET << 1
 |              |
 +--------------+
 |              |
 |     CODE     +- AW_EPR_OFFSET << 1
 |              |
 |              |
 +--------------+ 0x007fffff
 
     MPR-ROMS
 +--------------+ 0x00000000
 | FS_HEADER    |
 | FS_RECORD[1] +- (AW_MPR_RECORD_INDEX << 6)
 | FS_RECORD[2] |
 | FS_RECORD[3] +- (AW_MPR_FIRST_FILE_INDEX << 6)
 |     ...      |
 | FS_RECORD[N] |
 +--------------+- FS_RECORD[AW_MPR_FIRST_FILE_INDEX].FILE_ABS_OFFSET
 | FILE_0       | 
 | FILE_1       +- (AW_MPR_FILE_OFFSET << 1) + FS_RECORD[AW_MPR_FIRST_FILE_INDEX].FILE_ABS_OFFSET
 |     ...      |
 | FILE_N       |
 +--------------+ 0x07ffffff

Maison Ikkoku redump

Гуднесовский дамп Maison Ikkoku был уличен в мертвом зависании в определенном месте в самом начале игры. Самое интересное, что справлялся с этим ромом только японский Pasofami.

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

Как видно, в сплошном блоке размером 128 байт у каждого из байтов отсутствуют 4 старших бита. И таких блоков в дампе целых два. Не удивительно, что игра зависала в одном и том же месте. Возникает подозрение, что на Pasofami запускался не старый дамп, а новый, снятый с оригинального картриджа при помощи самого же Pasofami.

Махровая шизофрения

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

График

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