image

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

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


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


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

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

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

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

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

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


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


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


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

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

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


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


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

Ретро-кодер и владелец блога 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. Процедуру нужно вызывать после завершения игрового цикла, тогда на экране будет отображаться серая полоса.


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

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


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

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


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


Похоже, кроме белого экрана ничего нет. Кроме того, некоторые эмуляторы могут сообщить в строке состояния «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);

}

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


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

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


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

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


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

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


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


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


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


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


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

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


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

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

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

Комментарии (9)


  1. staticmain
    09.09.2019 13:39

    Я снова запускаю проект, но звука не слышу.

    Если мне не изменяет память, то это называется «отладка светодиодом».

    Такой вопрос — почему никто не делает удобных инструментов для разработки игр под NES/Sega сегодня? У них классная графика, если ей заниматься, готовый движок для скроллеров\платформеров, зачем клепать свой двиг для какого-то инди проекта, если можно разработать образ картриджа и распространять в стиме со вшитым эмулятором?


    1. SmilePic
      09.09.2019 14:19
      +1

      Там прямо готовый движок? Интересно.
      Сильно подозреваю, что чаще выбирают готовые движки, у которых нет ограничений NES/Sega. Соответственно, ниша для инструментов уже.


      1. staticmain
        09.09.2019 14:30

        Не совсем движок, но все же. Искоробочная поддержка режима отрисовки (скроллинг/экраны), искоробочная поддержка спрайтов, искоробочная поддержка палитр, искоробочная поддержка нормального унифицированного устройства ввода.


    1. shiru8bit
      09.09.2019 17:48

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


    1. DrMefistO
      09.09.2019 19:09

      Существует SGDK для сеги.


    1. da411d
      10.09.2019 13:26

      Сразу напомнилась старая добрая отладка алертами;)


  1. shiru8bit
    09.09.2019 17:54

    FCEUX предоставляет ещё другие средства отладки, не привязанные к ЯП. Самое главное, пожалуй — покадровая перемотка игры и быстрая перемотка, очень помогает отлавливать быстрые или наоборот, редкие, глюки, и воспроизводить их с помощью записи нажатий кнопок на джойстиках. Также там есть поддержка Lua, можно писать скрипты, которые следят за состоянием эмулируемой памяти и железа, и выводят что угодно на экран. Можно получать любую отладочную информацию, делать юнит-тесты, или даже написать AI, который будет играть в игру сам.

    Но есть и обратная сторона. FCEUX очень функционален, но не очень-то точен, критичный к таймингам код типа растровых эффектов в нём отлаживать нельзя. А почти все действительно точные эмуляторы не имеют встроенных отладчиков, либо возможности отладки там чисто номинальные. Единственное исключение сейчас и уже много лет — Nintendulator DX, форк с отладчиком. Насколько я помню, там есть даже поддержка таблицы символов cc65, чтобы иметь человеческие метки сразу в отладчике.


    1. VEG
      09.09.2019 22:51

      В Mesen отличный отладчик, и эмулятор сам по себе тактово-точный.


  1. sfrolov
    09.09.2019 20:12

    О, старый добрый код 6502!