Отладка игр для NES: как она происходит сегодня

в 10:21, , рубрики: C, fceux, Nes, ассемблер, отладка, разработка игр, разработка игр для nes, старое железо
image

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

Инструменты отладки

Один из лучших способов отладки кода — использование отладчика. В некоторых версиях эмуляторов FCEUX и Mesen есть встроенный отладчик, позволяющий прерывать в любой момент времени выполнение программы для проверки работоспособности кода.

Отладка игр для NES: как она происходит сегодня - 2

Отладчик эмулятора FCEUX

Стоит заметить, что этот способ больше подходит для продвинутых программистов, работающих с языком ассемблера. Но мы новички, поэтому будем писать на языке C (cc65). Разумеется, компилятор станет играть по своим собственным правилам, и нам будет трудно разбираться с машинным кодом, скомпилированным из кода на C.

Шестнадцатеричный редактор FCEUX

Допустим, нам нужно понаблюдать за какой-то переменной или массивом. Добавим к опциям компоновщика (ld65) такую строку: -Ln labels.txt

После компиляции проекта в его папке появится файл labels.txt. Просто откроем его в любой программе для просмотра текстов и поищем имя переменной, за которой мы хотим понаблюдать.

(Примечание: если вы объявили статическую переменную, то она не будет включена в этот список. Поэтому вместо static unsigned char playerX используйте unsigned char playerX)

Отладка игр для NES: как она происходит сегодня - 3

Теперь мы знаем адрес нужной переменной, неплохо. Давайте найдём её в отладчике. Запустим ROM игры в эмуляторе FCEUX. В меню Debug выберем пункт Hex Editor, а в открывшемся окне нажмём Ctrl + G и введём адрес нашей переменной:

Отладка игр для NES: как она происходит сегодня - 4

Нажмём OK, и курсор переместится к адресу, где расположена переменная. Давайте взглянем на неё:

Отладка игр для NES: как она происходит сегодня - 5

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

Изучите и другие полезные инструменты меню Debug эмулятора FCEUX, например, PPU Viewer, Name table Viewer и т.п.

Упрощение процесса отладки

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

Отладка игр для NES: как она происходит сегодня - 6

Работает идеально!

Ретро-кодер и владелец блога nesdoug Дуг Фрейкер создал похожий метод для использования экранных визуализаций в целях отладки. Показанная ниже процедура создаёт на экране серую линию, наглядно демонстрирующую степень нагрузки на ЦП:

// void gray_line(void);
// For debugging. Insert at the end of the game loop, to see how much frame is left.
// Will print a gray line on the screen. Distance to the bottom = how much is left.
// No line, possibly means that you are in v-blank.
 
_gray_line:

   lda <PPU_MASK_VAR
   and #$1f ;no color emphasis bits
   ora #1 ;yes gray bit
   sta PPU_MASK
 
ldx #20 ;wait
 
@loop2:
 
   dex
   bne @loop2
   lda <PPU_MASK_VAR ;normal
   sta PPU_MASK
   rts

Можно просто скопировать эту процедуру в своей код или включить в проект библиотеку nesdoug.h. Процедуру нужно вызывать после завершения игрового цикла, тогда на экране будет отображаться серая полоса.

Отладка игр для NES: как она происходит сегодня - 7

Сработало, но, похоже, у меня есть ещё один баг! Мы избавимся от него позже. А пока давайте двигаться дальше.

Мощь макросов

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

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

Отладка игр для NES: как она происходит сегодня - 8

Как это работает? Допустим, вам проект успешно скомпилировался, вы запускаете эмулятор со своей игрой, нажимаете на кнопку Start и…

Отладка игр для NES: как она происходит сегодня - 9

Похоже, кроме белого экрана ничего нет. Кроме того, некоторые эмуляторы могут сообщить в строке состояния «CPU jam!» Что же делать дальше?

Во-первых, нужно локализовать код, в котором происходит ошибка. И здесь в дело вступает мой макрос звука.

Мы точно знаем, что главное меню работает. Давайте посмотрим, что происходит после него:

playMainMenu();

player.lives = 9;
points = 0;
gameFlags = 0;

while(current_level<7 && player.lives>0)

{

       set_world(current_world);

      debugSound;

      playCurrentLevel();

}

У меня есть подозрение, что игра вылетает при выполнении процедуры set_world. Давайте проверим эту догадку. Я просто введу имя макроса в следующую строку после проверяемой процедуры.

Мы запускаем проект и… Я слышу звук! То есть эта процедура выполнилась успешно, и нам нужно проверить следующую: playCurrentLevel. Давайте переместим макрос отладки ниже:

while(current_level<7 && player.lives>0)

{

       set_world();

       playCurrentLevel():

      debugSound;

}

Я снова запускаю проект, но звука не слышу. Это означает, что процедура не завершена, и сбой происходит внутри неё.

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

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

if ( (getTile(objX, objY+16) || collide16() ) || (objsOX[i] && objY>objsOX[i]))

{

       debugRed;

       objsSTATE[i]=THWOMP_SMASH;

       objY=objsY[i]-=4;

       objsFRM[i]=0;

      sfx_play(SFX_THWOMP_SLAM_DOWN,2);

}

Если мы изменим здесь цвет палитры, то увидим, выполняется ли условие:

Отладка игр для NES: как она происходит сегодня - 10

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

Ядерный вариант

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

Взгляните на этого поражённого багом призрака — он нападает только когда персонаж находится близко к центру экрана:

Отладка игр для NES: как она происходит сегодня - 11

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

Я взял всё необходимое: карту экрана, массив с атрибутами метатайлов, код процедуры и просто вставил их в Visual Studio 2017:

Отладка игр для NES: как она происходит сегодня - 12

На PC код работал совершенно так же. Как оказалось, баг скрывался в процедуре, заполнявшей кэш для нахождения препятствий между игроком и врагом. Массив заполнялся некорректно. Я уверен, что здесь вместо 0x80 должен быть 0.

Отладка игр для NES: как она происходит сегодня - 13

Итак, я попытаюсь пошагово отладить код, чтобы выяснить, почему это происходит.

Отладка игр для NES: как она происходит сегодня - 14

Забавно, но похоже, что я выполнял действия в неправильной последовательности. Давайте исправим её и снова проверим массив!

Отладка игр для NES: как она происходит сегодня - 15

Кажется, теперь массив заполняется правильно. То есть мне достаточно просто исправить код cc65 и снова скомпилировать проект NES.

Итак, современные инструменты разработки способны помочь в отладке алгоритмов и устранении багов.

Избавляйтесь от багов спокойно

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

Хотите получать советы непосредственно от профессионалов ретро-разработки? Добр пожаловать в наш Discord!

Наша игра The Meating доступна здесь!

Автор: PatientZero

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js