ROMhacking.Ru
Ромхакинг: Архив новостей База ромхак-файлов Ромхакерская галерея Каталог разного Дополнительно: Поиск по сайту Зарубежный ромхакинг Новости эмуляции Cайт: Информация о сайте Общение: Форум Комментарии к материалам

Категории каталога

Порты [1]
Другое [21]

Наш опрос

Играете ли вы в свои проекты (переводы/хаки)?
Всего ответов: 794

База файлов

Главная » Файлы » Прочее » Другое

Комбинации клавиш на NES 1.0
[ Скачать с сервера (36.8 Kb) ] 04.02.2010, 12:59
Раздел 1: Последовательные наборы кнопок.

В этой статье я покажу как нужно взламывать и искать комбинации клавиш на Нинтендо. Начнем с того, что как происходит опрос клавиш джойстика. Первый джойстик сидит на адресе 4016, а второй на 4017. В большинстве случаев процесс опроса выглядит следующим обраом, пример из игры Ninja Turtles 3:

f92a: ad 16 40 LDA $4016 //чтение с порта джойстика
f92d: 85 0c STA $0C
f92f: 4A LSR
f930: 05 0C ORA $0C
f932: 4A LSR
f933: 36 08 ROL $08,X @ $0008 //помещение состояние джойстика в регистр 08

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

Вправо 01
Влево 02
Вниз 04
Вверх 08
А 80
В 40
START 10
SELECT 20

и их комбинации. Например START+SELECT выглядит как 20 И 10 = 30. То есть складываются логическим И.
Такая схема самая распространенная и встречается в подавляющем большинстве игр. Таких как Ninja Turtles 1-3, SuperContra 1-2, Contra, LifeForce и т.д..
Вторая по распространененности схема кодировок джойстика выглядит так:

Вправо 80
Влево 40
Вниз 20
Вверх 10
А 1
В 2
START 4
SELECT 8

Как видно кодировка отличается лишь тем, что в процесс опроса джойстика сканирование происходит с конца. То есть не
с кнопки Вправо, а с кнопки А. Такую систему легко отличить от первой тем что вместо команды ROL сдвига применяется обратная команда сдвига в другую сторону команда ROR. Пример такого опроса из игры CrossFire:

c35b: BD 16 40 LDA $4016,Х //чтение с порта джойстика
c35e: 85 23 STA $23
c360: 4A LSR
c361: 05 23 ORA $23
c363: 4A LSR
c364: 76 31 ROR $31,X @ $0031 //помещение состояние джойстика в регистр 31

Это два самых распространенных способа кодировок клавиш кнопок. Существуют также и экзотические формы кодировок клавиш, но уже после считывания их в память путем подмены одной кодировки на другую и опрос уже этих кодировок с тем, что записано в памяти. Наиболее яркий пример это игра The Hunt For Red October, которая кодирует повторно клавиши по схеме:

Вправо 52
Влево 4С
Вниз 44
Вверх 55
А 41
В 42
SELECT 53

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

СПОСОБ 1:Самый простой - поиск в памяти.

Во-первых, определимся где чаще всего игры проверяют комбинации. Правильно чаще всего игры опрашивают на титульном экране и в паузе. Все что нам нужно например на титульном экране войти в дебаггер эмулятора (Я пользуюсь FCEUltra).Сначало необходимо определить какая кодировка используется первая или вторая. Для этого нужно поставить бряк на 4016 и посмотреть какая команда используется ROR или ROL. Затем зайти в редактор памяти, снять дамп памяти RАМ, загрузить в редактор(Я применяю Translhextion), поставить скрипт комбинации которую определили и искать похожие на комбинации наборы. Для примера я взял Ninja Turtles 3. Загружаем ее в FCEUltra ждем когда игра дойдет до титульного экрана и идем в дебаггер. Ставим бряк на 4016 и смотрим. Используется команда ROL. Идем в редактор памяти Memory... и делаем дамп. В строке Memory File Dump вводим 0000 слева и FFFF справа. Тем самым мы полностью дампим память в файл. Теперь загружаем в Translhextion. Далее делаем скрип - текстовый файл с расширением tbl и заполняем его следующим содержимым:

08=u
04=d
01=r
02=l
80=A
40=B

Загружаем его в Translhextion - идем в Script и открываем наш файл. Ставим галочку на Thingy View Active и ищем подозрительные комбинации. Как правило они распологаются ниже середины дампа. После не долгих поисков видим такую строку по адресу A68A:

uuddlrlrAB dduurlrlAB uuddlrlrBA ABrlrldduu urrdddllllBA dddBAdddBA

Это и есть все комбинации которые опрашиваются игрой на заставке. Хотя в разных версиях игры (японская, американская) опрашиваются и не все.
Способ легог до автоматизма, но тем не менее далеко не все игры грузят сразу свои комбинации в память. Есть которые грузять только в процессе сравнения регистра джойстика.
Плюсы: простота метода
Минусы: Подходит далеко не ко всем играм.

СПОСОБ 2:Дамп памяти во время опроса регистра джойстика.

Для начала нам необходимо найти регистр куда считывается состояние джойстика. Затем найти то место где проверется на нажатие Start. И только после этого провести дамп выше описанным способом. Для примера возьмем игру Super C. Ставим бряк на 4016.

f92a: ad 16 40 LDA $4016 //чтение с порта джойстика
f92d: 85 0c STA $04
f92f: 4A LSR
f930: 05 0C ORA $04
f932: 4A LSR
f933: 36 08 ROL $00,X @ $0000 //помещение состояние джойстика в регистр 00

Наши это регистр 0. Заметим что здесь адрес ROL-ся. Значит схема кодировки 1-я. Теперь ставим бряк на 0 и убираем бряк на 4016. И смотрим в какие другие регистры заносится содержимое из этого регистра. Во-первых, отбрасываем все срабатывания на адрес f933 где содержимое регистра ROL-я. Для этого в FCEUltra добавляем бряк на f933 с галкой Exclude и нажимаем опять Run до тех пор пока не попадаем сюда:

fd06: A5 00 LDA $00
fd08: C5 02 CMP $02
fd0a: D0 1C BNE $FD28

Этот кусок нам не интересен. Нажимаем дальше Run пока не попадем сюда:

fd18: B5 00 LDA $00,X @$0000 = $00 //чтение из регистра где записано значение джойстика
fd1a: A8 TAY //помещение его в регистр Y
fd1b: 55 F7 EOR $F7,X @$00F7 = $00
fd1d: 35 00 AND $00,X @$0000 = $00
fd1f: 95 F1 STA $F1,X @$00F1 //заносится значение джойстика в регистр F1
fd21: 95 F5 STA $F1,X @$00F5 //заносится значение джойстика в регистр F5
fd23: 95 F3 STA $F3,X @$00F3 //заносится значение джойстика в регистр F3
fd25: 94 F7 STA $F7,X @$00F7 //заносится значение джойстика в регистр F7

Тем самым мы нашли где значение джойстика размножается по регистрам. В нашем в случае это F1, F5, F3 и F7. Теперь ставим брак на эти регистры и убираем бряк на 0. Теперь цель найти то место где один из этих бряков сравнивается с 10, то есть со START-ом. Не долго листая нажимая Run попадаем сюда:

e42f: A5 F5 LDA $F5 //чтение из регистра где записано значение джойстика
e431: 29 10 ADC #$10 //сравнение со START-ом

Теперь делаем дамп как в прошлом методе и грузим таблицу скрипта первого типа комбинаций клавиш и ищем последовательности похожие на комбинации. И находим по адресу A2BD такую последовательность:

rlduAB

Хотя если искать первым способом в памяти по этому адресу ее нет.

Плюсы: Можно выявить комбинаций у большего количества игр
Минусы: Более сложный чем первый метод.

СПОСОБ 3:Анализ регистра где записано значение джойстика

Это самый сложный метод и он более точно выявить скрытые опросы, так как непоредственно прослеживается весь путь от регистра до сравнения с комбинациями в памяти. Способ прост по смыслу, но индивидуален для каждых игр. Нужно найти регистр куда сохраняется значение джойстика и что с ним происходит дальше. Но как не трудно догадаться каждая игра в большинтстве случаев делает это по разному. Начнем с самого простого случая встречающегося очень часто. Речь идет о играх где сравнение происходит на лету. Такие игры как Ninja Turtles 3, Super C, Adventure Island 2,3 и т.д..

Пример 1: Ninja Turtles 3(U)
Доходим до заставки выбора числа игроков и ставим бряк на 4016 и смотрим:

f92a: ad 16 40 LDA $4016 //чтение с порта джойстика
f92d: 85 0c STA $0C
f92f: 4A LSR
f930: 05 0C ORA $0C
f932: 4A LSR
f933: 36 08 ROL $08,X @ $0008 //помещение состояние джойстика в регистр 08

Нашли регистр куда сохраняется значение джойстика это адрес 8. Так же обращаем внимание на то, что регистр ROL-ся. Значит схема кодировки первая. Ставим бряк на 8 и убираем бряк на 4016. Пропускаем срабатывания адреса 8 где он ROL-ся. И видим:

fd06: A5 08 LDA $08
fd08: C5 02 CMP $0A
fd0a: D0 1C BNE $F914

Не интересно. Нажимаем Run до следующего срабатывания бряка и попадаем сюда:

f904: B5 08 LDA $08,X @$0008 = $00 //чтение из регистра где записано значение джойстика
f906: A8 TAY //помещение его в регистр Y
f907: 55 FA EOR $FA,X @$00FA = $00
f909: 35 08 AND $08,X @$0008 = $00
f90b: 95 F8 STA $F8,X @$00F8 //заносится значение джойстика в регистр F8
f90d: 95 54 STA $54,X @$0054 //заносится значение джойстика в регистр 54
f90f: 95 FA STA $FA,X @$00FA //заносится значение джойстика в регистр FA
f911: 94 56 STA $56,X @$0056 //заносится значение джойстика в регистр 56

Нашли место куда заносится значение джойстика дальше. В данном случае размножается в регистры F8,54,FA,56. Так же замечу это очень распространенная структура используется для обоих джойстиков. Для первого Х=0, для второго Х=1. Ставим бряки на эти адреса и убираем бряк на 8. Срабатывает бряк и мы попадаем сюда:

806с: A5 F8 LDA $F8
806e: 05 F9 ORA $F9
8070: 29 20 AND #$20 //проверка на SELECT

Собственно здесь проверка на SELECT и нам не интересна. Нажимаем на Run и ждем срабатывания бряка и попадаем сюда:

8090: A5 F8 LDA $F8
8092: 05 F9 ORA $F9
8094: 29 20 AND #$10 //проверка на START

Тоже самое только проверка уже на START. Жмем Run дальше и попадаем сюда:

a648: A5 F8 LDA $F8 //чтение из регистра где записано значение джойстика
a64a: F0 28 BEQ $A674 //проверка на нулевое значение
a64c: BC DA 04 LDY $04DA,X @$04DA = $00 //грузится какое то число в Y
a64f: D1 00 CMP ($00),Y @$A68A = $08 //сравнивается значение джойстика с каким то числом

Вот оно! Нашли где проверка значения джойстика с тем, что в памяти находящимся комбинациями. Нажимая несколько раз на Run убиждаемся, что проверка осуществляется с 4-мя адресами: A68A, A697, A6A4, A6B1. Посмотрев в редакторе памяти и убеждаемся, что именно по этим адресам находится 4-е пароля:

uuddlrlrAB dduurlrlAB uuddlrlrBA ABrlrldduu

Хотя в памяти есть еще 2-а пароля, но именно в этой версии игры проверка только 4-х.

Пример 2: Alien 3(U)

Нашел пароль который набирается в паузе. Ставим игру на паузу и ставим бряк на 4016 и видим:

e7e5: ad 16 40 LDA $4016 //чтение с порта джойстика
e7e8: 4A LSR
e7e9: 66 28 ROR $28 @$0028 //помещение состояние джойстика в регистр 28

Видим, что значение джойстика заносится в регистр 28 причем оно ROR-ся, т.е. кодировка используется вторая. Ставим бряк на 28 и убираем на 4016. Попадаем сюда минуя попадания где адрес ROR-ся:

b3e1: A5 28 LDA $28 //чтение из регистра где записано значение джойстика
b3e3: A8 TAY //помещение его в регистр Y
b3e4: F0 50 BEQ $B436 //проверка на нулевое значение
B3e6: 29 08 AND #$08 //проверка на START
b3e8: D0 4C BNE $B436 //проверка, что нажал на START
b3ea: 98 TYA //Если не нажали на START возвращаем значение джойстика
b3eb: DD 37 B4 CMP $B437,X @$B437=$40 //проверяем его с тем, что в памяти

Смотри, а в памяти по адресу B437 находится комбинация:

LRUDLRABA

Проверяем ее и убеждаемся, что это пароль на переход на следующий уровень

Теперь разберем более сложный случай, когда сначало зачения джойстика записываются в регистры, а уже потом после нажатия на START проверяется. Хороший пример игра CrossFire.Ее и разберем.

Пример3: Cross Fire (J)
Доходим до заставки где горит надпись нажми на START и ставим бряк на 4016 и смотрим:

c35b: BD 16 40 LDA $4016,Х //чтение с порта джойстика
c35e: 85 23 STA $23
c360: 4A LSR
c361: 05 23 ORA $23
c363: 4A LSR
c364: 76 31 ROR $31,X @ $0031 //помещение состояние джойстика в регистр 31

Нашли регистр куда сохраняется значение джойстика это адрес 31. Так же обращаем внимание на то, что регистр ROR-ся. Значит схема кодировки вторая. Ставим бряк на 31 и убираем бряк на 4016. Пропускаем срабатывания адреса 31 где он ROR-ся. И видим:

c36c: A5 31 LDA $31 //чтение из регистра где записано значение джойстика
c36e: 48 PHA
c36f: 05 3B ORA $3B
c371: 45 3B EOR $3B
c373: 85 3A STA $3A //запись из регистра где записано значение джойстика в регистр 3А
c375: 68 PLA
c376: 85 3B STA $3B //запись из регистра где записано значение джойстика в регистр 3B

Видим, что значение джойстика записывается в регистр 3B и 3А. Ставим на них бряк. и попадаем сюда:

8d5b: A5 3A LDA $3A
8d5d: 29 08 AND #$08 //проверка на START

Собственно здесь проверка на START и нам не интересна. Нажимаем на Run и ждем срабатывания бряка и попадаем сюда:

8d2e: A5 3A LDA $3A //чтение из регистра где записано значение джойстика
8d30: F0 0E BEQ $8D40 //проверка на нулевое значение
8d32: AC 22 06 LDY &0622
8d35: C0 10 CPY #$10
8d37: B0 07 BCS $8D40
8d39: 99 23 06 STA $0623,Y @$63B //загрузка значения джойстика в один из регистров
8d3c: C8 INY //инкремент адреса куда писать новое значение джойстика

Мы нашли то место где не нулевые значения джойстика записываются в виде массива в память. Теперь убирираем все бряки и ставим бряк на адрес начальный 0623 и на адрес 0622. Нажимаем Run и переходим к игре и нажимаем Start.
Срабатывает бряк на 0622. Попадаем туда же, т.к. Start тоже заносится в память. Нажимаем Run. Срабатывает бряк на 0623. Там же. Опять жмем Run и попадаем сюда:

8dec: AD 22 06 LDA $0622 //сравнение нашего адреса с каким то в памяти
8def: DD 42 8E CMP $8E42,X @$8E42 = $0A
8df2: F0 0B BEQ $8DFF //если это число совпадает, то идем по адресу 8DFF

Что мы видим по адресу 8DFF:

8dff: 8A TXA
8e00: 48 PHA
8e01: A0 00 LDY #$10
8e03: E8 INX
8e04: B9 23 06 LDA $0623,Y //сравнивается нажатые кнопки с какими в памяти
8e07: DD 42 8E CMP $8E42,X @$8E43 = $04 //сравнение

Таким образом видим, что по адресу 0622 расположен счетчик нажатых клавиш, а начиная с 0623 массив нажатых кнопок. Смотри, что есть в дампе по адресу 8E43:

SSABBSBSA BBBABSSBAAS SBSBABBABBS где S - Select

И того 3-и комбинации.

Теперь перейдем к более сложному примеру Hunt for the Red October. В нем клавиши шифруются в другие значения.

Пример4: Hunt for the Red October, The [p1]
Найдем пароли которые набираются в паузе. Ставим игру на паузу и ставим бряк на 4016 и видим:

9f7d: AD 16 40 LDA $4016
9f80: 29 03 AND #$03
9f82: c9 01 CMP #$01
9f82: 26 47 ROL $47
...... //повторения
9fbc: AD 16 40 LDA $4016
9fbf: 29 03 AND #$03
9fbf: 26 47 ROL $47 //помещение состояние джойстика в регистр 47

Нашли регистр куда сохраняется значение джойстика это адрес 47. Так же обращаем внимание на то, что регистр ROL-ся. Значит схема кодировки первая. Тут восемь вхождений в 4016 и восемь сдвигов регистра 47. То есть по сути вместо цикла поставили подрят восемь считываний. Ставим бряк на 47 и убираем бряк на 4016. Пропускаем срабатывания адреса 47 где он ROL-ся. И видим:

ffde: A5 47 LDA $47
ffe0: 29 30 AND #$30 //проверка на Select и Start
ffe2: C9 30 CMP #$30
ffe4: D0 08 BNE $FFEE //Проверка одновременное нажатие кнопок Select и Start

В принципе не интересно. Стоит отметить, что когда ждут одновременное нажатие Select и Start ставять BNE, а если одну из кнопок, то ставят BEQ. Нажимаем на Run. И видим:

bbe8: A5 47 LDA $47
bbea: 29 10 AND #$10
bbec: D0 1C BNE $BC0A

Не интересно. Банальная проверка на Старт. Нажимаем на Run. И видим:

bbee: A5 47 LDA $47
bbf0: A2 00 LDX #$00
bbf2: 4A LSR //сдвиг вправо на 1
bbf3: 90 09 BCC $BBFE //проверка если не сдвинули до 0, то уходим на BBFE
bbf5: BD 0E BC LDA $BC0E,X //загружаем новый код клавиши
bbf8: 20 64 BD JSR $BD64 //уходим отсюда
bbfb: 90 07 BCC $BC04
bbfd: 60 RTS
bbfe: E8 INX //инкрементируем указатель на код клавиши расположенные по BC0E+Х
bbff: E0 08 CPX #$08 //проверка обошли ли все 8-м шифровок клавиш
bc01: 90 EF BCC $BBF2

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

bd64: 85 0F STA $0F //новый код заносится в регистр F.

И так попав в адрес bbee занесем в регистр 47 число например 0Х80 - код клавиши, ставим бряк на F на чтение . Нажимаем Run и видим:

bd80: B1 09 LDA($09),Y @$BCEA =$41 //грузится какой то код и
bd82: C5 0F CMP $0F @$000F =$42 //сравнивает с нашим

Попали в область где проверка нашей клавиши с массивом в памяти начало которого по адресу BCEA. Кодировки клавиш как я писал раньше:

Вправо 52
Влево 4С
Вниз 44
Вверх 55
А 41
В 42
SELECT 53

Посмотрим, что находится по адресу BCEA, сняв дамп и загрузив его в Translhextion. Включим скрипт по этой кодировке:

ABBARDULL ABSABSABSABSABSABBA ABSRLLRSBABBA S UDLRUDLRUDLRUDLRAAABBBSSSRRRUUUDDDLLLABBA
UDLRS UDLRUDLRUDLRABBA UUDDLLRR где S - Select

И того всего 8-м паролей. Из них два новых.

-- 13 янв 2010, 20:47 --

РАЗДЕЛ 2: Удерживаемые комбинации клавиш.

Пример1: Splatter_House_-_Wanpaku_Graffiti_(J). Комбинации при нажатии на RESET.

Теперь хотелось бы поговорить о поиск мест где необходимо зажимать определенные клавиши для включения чита. Особенно поиски мест где необходимо нажимать RESET на приставке. Как правило процесс опроса выглядит следующим образом на примере игры Splatter_House_-_Wanpaku_Graffiti_(J):

ac6b: A5 68 LDA $68 @ $0068 = $00 //чтение из регистра где записано значение джойстика причем со второго
ac6d: C9 C4 CMP #$C4 //сравнение с А+В+Вниз
ac6f: D0 09 BNE $AC7A //проверка на данную комбинацию

Как же найти процедуру проверки на зажатие кнопкок сразу после сброса приставки. А очень просто. Поставте бряк на 4016 и 4017 и в дебаггере нажмите Reset. Сразу попадаем в процедуру опроса джойстика:
Для бряка на 4016:

e1c2: AD 16 40 LDA $4016 @ $4016 = $40 //чтение с порта джойстика 1
e1c5: 85 02 STA $02
e1c7: 4A LSR
e1c8: 05 02 ORA $02 @ $0002 = $00
e1ca: 4A LSR
e1cb: 26 00 ROL $00 @ $0000 = $0F //помещение состояние джойстика в регистр 00

Для 4017

e1cd: AD 17 40 LDA $4017 @ $4017 = $40 //чтение с порта джойстика 2
e1d0: 85 02 STA $02
e1d2: 4A LSR
e1d3: 05 02 ORA $02 @ $0002 = $00
e1d5: 4A LSR
e1d6: 26 01 ROL $01 @ $0001 = $FF //помещение состояние джойстика в регистр 01
e1d8: 88 DEY
e1d9: D0 E7 BNE $E1C2

Как видно для первого джойстика состояние записывается в 0, второго в 1. Ставим бряк на 0 и 1 и снимаем с 4016 и 4017, а так же исключаем бряки на e1cb и e1d6 где адреса 0 и 1 ROL-ся. Нажимаем Run и попадаем сюда:

e1e6: B5 00 LDA $00,X @ $0000 = $00 //считывание состояние 1-го джойстика
e1e8: A8 TAY //копирование состояние джойстика из регистра А в Y
e1e9: 55 67 EOR $67,X @ $0067 = $00
e1eb: 35 00 AND $00,X @ $0000 = $00
e1ed: 95 65 STA $65,X @ $0065 //помещение состояние джойстика в регистр 65
e1ef: 94 67 STY $67,X @ $0067 //помещение состояние джойстика в регистр 67
e1f1: 60 RTS

Это стандартная структура где Х при 0 для 1-го джойстика, а 1 для 2-го. То есть для 1-го джойстика будут адреса 65,67 а для второго 66,68. Ставим бряки еще для 65-68 адресов. 0 и 1 не убираем, так как бывает что и с на тоже проверка организуется. Нажимаем Run и попадаем сюда:

ac6b: A5 68 LDA $68 @ $0068 = $00 //чтение из регистра где записано значение джойстика причем со второго
ac6d: C9 C4 CMP #$C4 //сравнение с А+В+Вниз
ac6f: D0 09 BNE $AC7A //проверка на данную комбинацию
ac71: A9 09 LDA #$09
ac73: 85 70 STA $70
ac75: A9 00 LDA #$00
ac77: 85 71 STA $71

Вот и нашли где сразу после RESET опрашивается 2-й джойстик на комбинацию А+В+Вниз. Нажме еще Run может еще опрос есть:

ac7a: A5 67 LDA $67 @ $0067 = $00 //чтение из регистра где записано значение джойстика1
ac7c: C9 A8 CMP #$A8 //сравнение с А+Select+Вверх
ac7e: D0 09 BNE $AC89 //проверка на данную комбинацию
ac80: A9 05 LDA #$05
ac82: 85 70 STA $70
ac84: A9 00 LDA #$00
ac86: 85 71 STA $71

И не ошиблись есть еще проверка на А+Select+Вверх 1-го джойстика. Так же стоит обратить внимание на то что при разных комбинация есть только одно место которое отличается строка ac71 и ac80. В первом случае грузится 9, во втором 5. Это место где выбирается что приставке грузить дальше. Путем подбора выяснил, что если заменить 9 на 8, то вместо TEST MODE загрузится SCENE SELECT MODE. Судя по дампу памяти в роме есть еще STAGE SELECT MODE, но добратся до него не удалось.

Пример2: Battletoads (U). Проверка комбинаций после Start.

Бывает так, что комбинация при одновременном нажатии Start и еще каких либо проверяются не сразу. Одну из таких игр и рассмотрим.
Ставим бряк на 4016-4017:

Для 4016
8d83: AD 16 40 LDA $4016 @ $4016 = $40 //чтение с порта джойстика 1
8d86: 6A ROR
8d87: 26 15 ROL $15 @ $0015 = $00 //помещение состояние джойстика в регистр 15

Для 4017
8d89: AD 17 40 LDA $4017 @ $4017 = $40 //чтение с порта джойстика 2
8d8c: 6A ROR
8d8d: 26 16 ROL $16 @ $0016 = $00 //помещение состояние джойстика в регистр 16

Ставим бряк на 15-16, убираем на 4016-4017, делаем исключение для 8d87 и 8d8d.Запускаем Run и попадаем сюда:

8d92: A5 15 LDA $15 @ $0015 = $00 //чтение из регистра где записано значение джойстика1
8d94: AA TAX //копирование состояние джойстика из регистра А в Х
8d95: 45 29 EOR $29 @ $0029 = $00
8d97: 86 29 STX $29 //помещение состояние джойстика1 в регистр 29
8d99: 25 15 AND $15 @ $0015 = $00
8d9b: 85 2B STA $2B //помещение состояние джойстика1 в регистр 2B
8d9d: A5 16 LDA $16 @ $0016 = $00 //чтение из регистра где записано значение джойстика2
8d9f: AA TAX //копирование состояние джойстика из регистра А в Х
8da0: 45 2A EOR $2A @ $002A = $00
8da2: 86 2A STX $2A //помещение состояние джойстика2 в регистр 2А
8da4: 25 16 AND $16 @ $0016 = $00
8da6: 85 2C STA $2C //помещение состояние джойстика2 в регистр 2С

Размножение из адресов 15, 16 в 29,2В и 2А,2С соответственно. Ставим бряки на 29,2В и 2А,2С.Запускаем Run и попадаем сюда:

e5ca: A5 2B LDA $2B @ $002B = $00 //чтение из регистра где записано значение джойстика1
e5cc: 05 2C ORA $2C @ $002C = $00
e5ce: 29 10 AND #$10 //сравнение со START-ом
e5d0: D0 0C BNE $E5DE

Нашли где проверка на START. Теперь запишем в регистр 2В число 10. Для этого заходим в Memory... и в строке MemoryPoke ставим число 10 и нажимаем Poke Me!. Это эмуляция нажатия START. Запускаем Run и попадаем сюда:

e5eb: B5 2B LDA $2B,X @ $002C = $00 //чтение из регистра где записано значение джойстика2
e5ed: 29 10 AND #$10 //сравнение со START-ом

Пропускаем так как первый игрок нас не интересует. Запускаем Run и попадаем сюда:

e5eb: B5 2B LDA $2B,X @ $002B = $10 //чтение из регистра где записано значение джойстика1
e5ed: 29 10 AND #$10 //сравнение со START-ом

Видимо проверка есть вместе 2-х джойстиков на START и по отдельности. Здесь отдельная проверка для первого джойстика. Значение в регистре сохранено и равно по прежнему 10. Пропускаем. Запускаем Run и попадаем сюда:

e611: B5 29 LDA $29,X @ $0029 = $00 //чтение из регистра где записано значение джойстика1
e613: C9 D4 CMP #$D4 //проверка на А+В+Вниз+Start
e615: D0 0B BNE $E622
e617: A9 05 LDA #$05 //грузится в случае правильного набора 5 жизней
e619: 95 11 STA $11,X @ $0011 //здесь память жизней

Вот и нашли где после нажатия START проверяется комбинация клавиш.

-- 13 янв 2010, 20:47 --

РАЗДЕЛ 3: ДОБАВЛЯЕМ СВОИ КОМБИНАЦИИ КЛАВИШ

Используя знания предыдущих разделов можно самому добавлять комбинации клавиш. Скажем кто мешает добавить пополнение энергии комбинацией Вверх+START. Как это делается в этом разделе и рассмотрим. Но сначало рассмотрим несколько нюансов связанных с тем, что игры на NES в память полностью не грузятся, а что грузить определяет маппер. Всего мапперов очень много и разбираться с ними не иммет мысла и перспектив в этом вопросе. Поэтому мы пойдем другим путем. А именно будет вставлять свой код в текущий в данный момент опроса блок памяти. Но чтобы знать какой блок в данный момент грузится необходимо опять обратится к маперру? Нет, можно и это обойти. Для этого необходимо найти то место где ведется опрос на Start.И пределы области памяти 0000-FFFF где находится этот опрос и будет текущий. И знать как маппер блоками рулит нет надобности.

Пример1: Little Mermaid, The (U) добавим комбинацию Вверх+START для пополнения жизни.

Для начала необходимо найти адрес где текущая жизнь хранится с помощью VirtualNes. В данной игре это адрес В1. Теперь найдем опрос на Start. Ставим игру на паузу и ставим бряк на 4016. Запускаем Run и попадаем сюда:

c123: AD 16 40 LDA $4016 @ $4016 = $40 //чтение из регистра джойстика1
c126: 4A LSR
c127: 26 14 ROL $14 @ $0014 = $00 //помещение состояние джойстика в регистр 14
c129: 4A LSR
c12a: 26 00 ROL $00 @ $0000 = $F5 //помещение состояние джойстика в регистр 0

Выбираем регистр 14 и ставим бряк на него, убираем на 4016, исключаем c127. Запускаем Run и попадаем

c13a: 05 14 ORA $14 @ $0014 = $00
c13c: 85 14 STA $14 //помещение состояние джойстика в регистр 14
c13e: A5 01 LDA $01 @ $0001 = $00
c140: 05 15 ORA $15 @ $0015 = $00
c142: 85 15 STA $15
c144: A2 01 LDX #$01
c146: B5 14 LDA $14,X @ $0014 = $00 //чтение из регистра где записано значение джойстика1
c148: A8 TAY //копирование состояние джойстика из регистра А в Y
c149: 55 16 EOR $16,X @ $0016 = $00
c14b: 35 14 AND $14,X @ $0014 = $00
c14d: 95 14 STA $14,X @ $0014 //помещение состояние джойстика в регистр 14
c14f: 94 16 STY $16,X @ $0016 //помещение состояние джойстика в регистр 16

Видим, что есть еще регистр где хранится состояние джойстика1. Это регистр 16. Ставим бряк на 16.Нажимаем Run пока не попадем сюда:

eb70: A5 14 LDA $14 @ $0014 = $00 //чтение из регистра где записано значение джойстика1
eb72: 29 10 AND #$10 //сравнение со START-ом
eb74: D0 09 BNE $EB7F //если нажали START идем по адресу EB7F.
eb76: 20 02 EC JSR $EC02
eb79: 20 AD FF JSR $FFAD
eb7c: 4C 70 EB JMP $EB70
eb7f: A9 1C LDA #$1C
eb81: 20 A0 FC JSR $FCA0

То что нужно. Здесь опрос и переход если нажали START. Теперь необходимо взять 3 байта под переход JMP. Команду BNE мы взять не можем, т.к. это по сути команда пропуска байт если выполнилось условие от того место откуда она была вызвана. Смотрим что идет по перехода EB7F:

eb7f: A9 1C LDA #$1C
eb81: 20 A0 FC JSR $FCA0

Вот это мы можем захватить под переход, так как здесь переход прямой по адресу FCA0. Теперь необходимо найти место в роме где можно воспроизвести то, что мы позаимствовали под переход JMP. Смотрим ниже адреса eb81 и ищим повторяющиеся байты FF не менее 20-30 раз. Для этого прокручиваем в дебаггере бегунок вниз и смотрим. Смотрим и находим по адресу FE8D. Там достаточно места для написания нашего кода. Вообще смотреть свободное место лучше начинать с конца. Таким образом нам необходимо заменить код :

eb7f: A9 1C LDA #$1C
eb81: 20 A0 FC JSR $FCA0

На следующий

eb7f: 4C 8D FE JMP $FE8D //переход по нашему адресу FE8D там где будет расположен наш код
eb82: EA NOP //пустая команда под заполение оставшегося места
eb83: EA NOP //пустая команда под заполение оставшегося места

Для того чтобы заменить старый на новый необходимо в роме в нЕХ редакторе найти последовательность байт:
4C 70 EB A9 1C 20 A0 FC и заменить ее на 4C 70 EB 4C 8D FE EA EA.

Теперь необходимо вставить наш код по адресу FE8D. А код такой:

FE8D: A5 16 LDA $16 //грузится значение джойстика1
FE8F: C9 18 CMP #$18 //сравнение с Вврех+Start
FE91: D0 04 BNE $FE97 //если не нажата комбинация-идем к старому коду который убрали под переход JMP
FE93: A9 05 LDA #$05 //если нажали комбинацию, то грузим жизнь 5
FE95: 85 B1 STA $B1 //помещаем ее в регистр где жизнь текущая хранится
FE97: A9 1C LDA #$1C //далее выполняем старый код который выбрали по JMP
FE99: 20 A0 FC JSR $FCA0
FE9C: 4C 84 EB JMP $EB84 //переход обратно откуда перешли суда минуя переход который вставили

Заменяем старый код на новый найдем где же пустое место с FF расположено в роме. Для этого в эмуляторе посморим, что расположено выше адреса FE8D:

04 08 10 20 40 80 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Эту последовательность и ищем в НЕХ редакторе и заменяем ее на:

04 08 10 20 40 80 A5 16 C9 18 D0 04 A9 05 85 B1 A9 1C 20 A0 FC 4c 84 EB

Сохраняем изменения в НЕХ редакторе. Запускаем ром нажимаем паузу и затем Вверх+Start и у вас 5 текущих жизней. Работает.

Пример2: Snake_Rattle_n_Roll(U) добавим комбинацию Вниз+START чтобы жизней стало 9 и таймер установить на 90.

Первоначально находим где хранится жизнь и таймер - 3DF и десятые значения таймера хранятся в СЕ. Теперь нужно найти где происходит проверка на Start. Как обычно ставим бряк на 4016, затем на регист где хранится состояние джойстика(у меня адрес 4) и находим все адреса где хранится значение джойстика. У меня получилось 4, 16 и 18. Теперь ставим бряк еще и на 16,18 и ищем где проверка на START пока не находим такой кусок кода:

8dac: A5 18 LDA $18 @ $0018 = $00 //грузится значение джойстика1
8dae: 05 19 ORA $19 @ $0019 = $00
8db0: 29 10 AND #$10 //сравнение со START-ом

Теперь нужно определиться что будет заимствовать под переход. Лучше все здесь подходит операция загрузки LDA и логическая операция ORA их и заменим на JMP. Необходимо так же знать куда будем переходить - найдем свободную область памяти куда можно вставить свой код. Чесно сказать в этой игде свободного места почти нет. Единственное место это область заполненная нулями по адресу ECC1. Ее и будем использовать. Таким образом известен адрес куда будем идти. Меняем выуказанный код на свой:

8dac: 4C C1 EC JMP $ECC1 //переход по адресу ECC1
8daf: EA NOP //свободное место заполняем NOP
8db0: 29 10 AND #$10

Для того чтобы заменить старый на новый необходимо в роме в нЕХ редакторе найти последовательность байт:
A5 18 05 19 29 10 и заменить ее на 4C C1 EC EA 29 10

Далее необходимо 00 по адресу ECC1 заменить на наш код:

ecc1: A5 16 LDA $16 @ $0016 = $00 //берем второй регистр 16, а не 18 для загрузки значения джойстика1
ecc3: C9 14 CMP #$14 //сравнение с Вниз+Start
ecc5: D0 09 BNE $ECD0 //если не нажата комбинвация, то выполняем старый код который заменили на JMP
ecc7: A9 09 LDA #$09 //загружаем жизнь равную 9
ecc9: 8D DF 03 STA $03DF //делаем жизнь по адресу 3DF равную 9
eccc: A9 09 LDA #$09 //загружаем таймер равный по десяткам 9
ecce: 85 CE STA $CE //делаем таймер по адресу СЕ равный по десяткам 9
ecd0: A5 18 LDA $18 @ $0018 = $00 //старый код который заменили выше на JMP
ecd2: 05 19 ORA $19 @ $0019 = $00
ecd4: 4C B0 8D JMP $8DB0 //уходим обратно откуда ушли минуя наш JMP

Заменяем старый код на новый найдем где же пустое место с 00 расположено в роме. Для этого в эмуляторе посморим, что расположено выше адреса ECC1:

48 3F 04 04 04 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Эту последовательность и ищем в НЕХ редакторе и заменяем ее на:

48 3F 04 04 04 01 01 01 00 A5 16 C9 14 D0 09 A9 09 8D Df 03 A9 09 85 CE A5 18 05 19 4C B0 8D

Сохраняем изменения в НЕХ редакторе. Запускаем ром нажимаем паузу и затем Вниз+Start и у вас 9 жизней и таймер снова становится больше 90. Работает.

Пример3:Darkwing Duck (U) [o1] добавим комбинацию в паузе Верх,Вниз,Влево,Вправо - сделаем9 жизней при ее вводе

Пример болле сложный и места потребуется гораздо больше. Не случайно я выбрал эту игру. Во-первых, в ней нет комбинаций вообще, а во-вторых, в ней есть достаточно места чтобы вставить достаточно большой кусок кода. Для начала найдем где хранится жизнь - у меня это адрес 5Е6. Жизнь хранится в виде числа жизней + 0х70. То есть жизней 3, а хранится 73. Теперь найдем где происходит опрос на нажатие START. У меня это вот это место:

8fc4: A5 14 LDA $14 @ $0014 = $00
8fc6: 29 10 AND #$10
8fc8: F0 ED BEQ $8FB7

Ищем место куда можно вставить 43 байта. Очень много места по адресу FC4B. Определились куда будем пихать наш код. Теперь заменим кусок кода по адресу 8fc4-8fc7 на переход:

8fc4: 4C 4B FC JMP $FC4B
8fc7: EA NOP
8fc8: F0 ED BEQ $8FB7

Для того чтобы заменить старый на новый необходимо в роме в нЕХ редакторе найти последовательность байт:
A5 14 29 10 F0 ED и заменить ее на 4C 4B FC EA F0 ED

Далее начиная с адреса FC4B вставляем свой код:

fc4b: AD FF 1F LDA $1FFF @ $1FFF = $00 //грузим счетчик введеных правильно комбинаций
fc4e: AA TAX //грузим его в регистр Х
fc4f: A5 14 LDA $14 @ $0014 = $00 //грузим состояние джойстика
fc51: DD 73 FC CMP $FC73,X @ $FC83 = $FF //сравниваем его с правильной комбинацией
fc54: D0 13 BNE $FC69 //если не правильно выполняем старый код который заменили на JMP
fc56: E8 INX //если правильно ввели увеличиваем счетчик правильно введеных комбинаций
fc57: 8A TXA //помещаем его в регистр А
fc58: 8D FF 1F STA $1FFF //заносим увеличенный счетчик по адресу 1FFF
fc5b: C9 04 CMP #$04 //сравниваем правильное введеное количество с 4.Всего 4-е комбинации
fc5d: D0 0A BNE $FC69 //если не все правильно ввели, то выполняем старый код
fc5f: A9 00 LDA #$00 //если всен ввели, то обнуляем счетчик введеных комбинаций
fc61: 8D FF 1F STA $1FFF //заносим обнуленный счетчик по адресу 1FFF
fc64: A9 79 LDA #$79 //делаем жизнь раную 9
fc66: 8D E6 05 STA $05E6 //грузим новое число жизней
fc69: A9 FF LDA #$FF //заносим число которое было в Х обратно, там было FF
fc6b: AA TAX //и в Х его
fc6c: A5 14 LDA $14 @ $0014 = $00 //это старый код который мы заменяли выше на JMP
fc6e: 29 10 AND #$10
fc70: 4C C8 8F JMP $8FC8 //уходим отсуда обратно минуя наш JMP
fc73: 08 PHP //код кнопки Вверх
fc74: 04 .db $04 //код кнопки Вниз
fc75: 02 .db $02 //код кнопки Влево
fc76: 01 00 ORA ($00,X) @ $0000 = $76 //код кнопки Вправо

Теперь по подробнее рассмотрим, что и откуда я взял для написания этого кода. Первое, я взял под хранение счетчика правильно нажатых кнопок адрес 1FFF. Как правило этот адрес редко используется. В этом легко убедиться поставив бряк на этот адрес и начать с самого начала с загрузки игры и до паузы - бряк не сработал. Можно и другой, но нужно проверять используется ли этот адрес во время игры. Далее для того, чтобы смещаться относительно базового адреса и сравнивать код клавиши с тем что в памяти можно использовать X или Y. Я взял Х. Предварительно я посмотрел чему равно значение до вхождения в наш кусок кода. А равно оно FF. Так же убедился, что значение не меняется поиграв немного и снова войдя в наш кусок кода. По прежнему FF. И перед тем как выполнить старый код который мы заменили на JMP я вернул то самое FF. Алгоритм прост. Сначало грузится счетчик правильно введеных комбинаций кнопок по адресу 1FFF в регистр Х. Затем грузится состояние джойстика. Оно сравнивается с тем, что записано в памяти + смещение на величину введеных уже комбинаций. Если они не равны, то возвращается старое значение Х и выполняется старый код. А если они равны увеличивается счетчик введеных клавиш и сохраняется по адресу 1FFF. Далее оно сравнивается с максимальным числом комбинаций которое нужно ввести. Если оно меньше, то возвращается старое значение Х и выполняется старый код. Если же они они одинаковы, то счетчик обнуляется и сохраняется по адресу 1FFF. Потом заносится новое значение жизни 79 по адресу 5E6, возвращается старое значение Х и выполняется старый код. И наконец выходим из программы обратно минуя наш переход JMP.

Заменяем старый код на новый найдем где же пустое место с FF расположено в роме. Для этого в эмуляторе посморим, что расположено выше адреса fc4b:

40 03 60 01 02 04 08 10 20 40 80 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Эту последовательность и ищем в НЕХ редакторе и заменяем ее и все что дальше на:

40 03 60 01 02 04 08 10 20 40 80 AD FF 1F AA A5 14 DD 73 FC D0 13 E8 8A 8D FF 1F C9 04 D0 0A A9 00 8D FF 1F A9 79 8D E6 05 A9 FF AA A5 14 29 10 4C C8 8F 08 04 02 01

Сохраняем. Проверяем. Идем в паузу, Нажимаем Верх,Вниз,Влево,Вправо и видим, что жизнь с 3-х сменилась на 9. Работает.

Категория: Другое | Добавил: ЯковлевВиктор | Автор: ЯковлевВиктор
Просмотров: 4156 | Загрузок: 817 | Комментарии: 1 |
Всего комментариев: 1
+2  
1 CARI (11.02.2010 21:06)
Полезная дока.
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Поиск

Галерея

Партнеры сайта

  • Создание игр на GcUp.ru
  • Всё об играх на GamesFAQ