| Разработка и ромхакинг > Разработка игр |
| Хранение клона OAM таблицы в PRG-ROM NES |
| << < (3/3) |
| Howard Phillips:
Спасибо, сделал прерывания NMI активными во время выключения экрана, и "рывок" фон пропал, все четко. Еще добавил ожидание NMI перед отключением экрана вот так: --- Код: ---void All_Off (void) { while (NMindex_flag == 0); // wait till NMI NMindex_flag = 0; PPU_MASK = 0x00; } void All_On (void) { while (NMindex_flag == 0); // wait till NMI NMindex_flag = 0; PPU_MASK = b0001_1110; } --- Конец кода --- Но во время отключения экрана видно, что экран успевает "моргнуть", т.е. почернеть на один кадр, а потом картинка возвращается. Может у меня не успевает отработать код за один кадр? ПС: И я нашел хороший способ узнать, как проверить сколько я потратил ROM. Можно открыть ROM в редакторе тайлов YY-CHR и посмотреть сколько пустых страниц в файле. Очень просто и наглядно. |
| Sharpnull:
--- Цитата: Howard Phillips от 16 Апрель 2023, 22:12:28 ---Но во время отключения экрана видно, что экран успевает "моргнуть", т.е. почернеть на один кадр, а потом картинка возвращается. --- Конец цитаты --- Здесь мне не понятно что происходит, что вызывается и когда, как я понял: вызывается All_Off(), следующий кадр чёрный, след. кадр отображается как будто не было откл. экрана, а после кадры чёрные до вкл. экрана. Может в NMI происходит изменение PPUMASK, но тогда где снова PPUMASK = 0. Ожидание VBlank у вас тоже странное, ожидать пока NMindex_flag не станет != 0, но где установка NMindex_flag = 0 до вызова All_Off() и All_On(). Например, делают так: --- Код: ---NMI: INC $xx RTI WaitVBlank: LDA $xx @loop: CMP $xx BEQ @loop RTS --- Конец кода --- |
| Howard Phillips:
Вот пример вызова включения и выключения экрана: --- Код: --- if (parameters_shown == false) { All_Off (); draw_all_parameters (); clear_status (); hide_weapon_submenu (); PPU_ADDRESS = 0x00; PPU_ADDRESS = 0x00; All_On (); parameters_shown = true; } --- Конец кода --- NMindex_flag изменяет в обработчике прерывания: --- Код: ---void NMI (void) { ++NMindex_flag; ++Frame_Count; OAM_ADDRESS = 0; OAM_ADDRESS = 0; OAM_DMA = 0x02; // push sprite data to OAM from $200-2ff // PPU_CTRL = 0x90; // PPU_MASK = temp_PPU_MASK; // screen on // Сброс скрола SCROLL = 0x00; SCROLL = 0x00; // Выход из прерывания // Без него не получится реализовать корректный выход из прерывания asm ("rti"); } --- Конец кода --- После вызова All_Off() экран чернеет и картинка возвращается только после вызова All_On(). |
| Sharpnull:
Судя по коду, перед вызовом All_Off() у вас в NMindex_flag может быть любое значение от 0 до 255, тогда while (NMindex_flag == 0); будет в большинстве случаев не ждать и ждать до VBlank только с NMindex_flag == 0 (UPD) перед вызовом All_Off(). Почему у вас работает All_On() тоже не понятно, если только код срабатывает за один кадр между All_Off() и All_On(). У вас есть Frame_Count, вы можете ожидать как я описывал, на Си наверно так (лучше на ASM): --- Код: ---int prev_Frame_Count = Frame_Count; // Либо во временную переменную while (Frame_Count == prev_Frame_Count); --- Конец кода --- --- Цитата: Howard Phillips от 16 Апрель 2023, 23:15:50 ---После вызова All_Off() экран чернеет и картинка возвращается только после вызова All_On(). --- Конец цитаты --- Но вы писали про моргание. |
| Howard Phillips:
Да, вы правы, у меня была странноватая логика ожидания прерывания. Это все остатки кода на основе которого я изучал разработку под NES на Си. Переписал функцию ожидания прерывания на ассемблере вот так: --- Код: ---; Ф-я ожидает прерывания NMI (конец кадра) _Wait_Vblank: lda $2002 bpl _Wait_Vblank rts --- Конец кода --- bpl проверяет отрицательность регистра, а в старшем бите у нас как раз бит который флагует о наличии прерывания. Заменил все вызовы while (NMindex_flag == 0); // wait till NMI NMindex_flag = 0; на вызов функции: Wait_Vblank (); Логика не сломалась, все работает, но "почернение" кадра не пропало. Морганием я это называл, подразумевая, что происходит одна итерация моргания, как один раз закрыть глаза, а потом открыть. Сейчас функции включения и выключения экрана выглядят так: --- Код: ---void All_Off (void) { Wait_Vblank (); // wait till NMI PPU_MASK = 0x00; } void All_On (void) { Wait_Vblank (); // wait till NMI PPU_MASK = b0001_1110; } --- Конец кода --- А вообще после отключения отображения спрайтов и фона, должны ли они оставаться на экране? Может я что-то не так понимаю? Может экран и должен становиться черным? NMindex_flag в итоге я вообще выкинул из программы, мне тоже этот костыль не нравился изначально, но раньше было лень рефакторить. |
| Sharpnull:
--- Цитата: Howard Phillips от 17 Апрель 2023, 00:30:13 ---Переписал функцию ожидания прерывания на ассемблере вот так: --- Конец цитаты --- Ожидать через lda $2002 bpl можно только с отключенным NMI, иначе может быть пропуск кадра. С вкл. NMI напишите _Wait_Vblank так: --- Код: --- LDA Frame_Count @loop: CMP Frame_Count BEQ @loop RTS --- Конец кода --- --- Цитата: Howard Phillips от 17 Апрель 2023, 00:30:13 ---А вообще после отключения отображения спрайтов и фона, должны ли они оставаться на экране? --- Конец цитаты --- После PPUMASK = 0 будет весь фон одного цвета фона (у вас видимо чёрный). Если хотите "заморозить" картинку, то придётся без откл. экрана записывать в VBlank в течение нескольких кадров, но т. к. вы меняете фон, это будет заметно, остаётся делать двойную буферизацию: отображать один Nametable и изменять другой за пределами экрана, а потом в VBlank менять скрытый на отображаемый Nametable через Scroll ($2005) и Base nametable address ($2000), но вряд ли вы так будете делать. |
| Howard Phillips:
Заменил функцию ожидания на ваш код, но визуально разницу не заметил в работе игры. Но стабильность не помешает, я согласен. А про борьбу с черным экраном я так и думал, что тут только или буферизация или фон редактировать маленькими кусочками за несколько срабатываний NMI. Самые критичные моменты моменты я потом перепишу без отключения экрана. Сейчас все вроде бы работает как надо. Можно дальше двигаться к релизу. |
| Howard Phillips:
Здравствуйте. При разработке столкнулся с очередной проблемой. Решил сделать раздельный скроллинг для вывода интерфейса вверху экрана (выставляю позицию камеры в 0, вывожу интерфейс, потом на при срабатывании флага нулевого спрайта возвращаю правильное положение камеры). Вот код: --- Код: ---// Обработчик прерывания void NMI (void) { ++Frame_Count; OAM_ADDRESS = 0; OAM_ADDRESS = 0; // Младший полубайт задает адрес RAM, // из которого будет заполняться OAM видеопамяти // В нашем случае копия ОАМ хранится по адресу $200-2ff // 0х02 - указывает адрес $200 из ОЗУ OAM_DMA = 0x02; SCROLL = 0; SCROLL = 0; // Выход из прерывания asm ("rti"); // Вот основной цикл тестовый while (1) { // ЖДем конец кадра Frame_Count_old = Frame_Count; while (Frame_Count == Frame_Count_old); // Меняем скроллинг SCROLL = 0; SCROLL = 0; while ( (PPU_STATUS & b0100_0000) == 0); // Обновление скроллинга SCROLL = h_scroll; SCROLL = 0; // Двигаем камеру ++X_hero; if (X_hero > 127 && X_hero < 383) { h_scroll = (unsigned char)(X_hero - 128); //h_scroll += 5; } else { if (X_hero >= 383) { h_scroll = 255; } } } } --- Конец кода --- Для срабатывания нулевого спрайта я в фон вывел цифру 0 и сверху наложил спрайт с красным патроном (его видно на скрине). И так, в коде работает только обратчик прерывания, изменения позиции камеры и прокрутка камеры для проверки. Я убрал все лишнее для отладки. Все работает, но не совсем верно. Текст интерфейса (количество патронов и т.д.) выводится через кадр, т.е. мерцает. Судя по всему каждый 2-й кадр интерфейс выводится в неправильное положение скроллинга и получается мерцание. Еще есть у меня баг со скроллигом, когда движется камера, некоторые цвета заметно темнеют. Возможно это связанные проблемы. Прикрепляю скрин на котором я поймал правильный вывод интерфейса. Четко видно, что вывод интерфейса работает через кадр. Я из кода все лишнее выкинул и непонятно, что может ломать скроллинг. Очень надеюсь на помощь, так как спрайтами меню выводить не хочется (они почти закончились). |
| Sharpnull:
Howard Phillips, бит Sprite 0 Hit очищается на "dot 1 of the pre-render line" (в Mesen это Scanline: -1, после 260), а выход из NMI происходит раньше (для NTSC начинается со scanline 241 и почти сразу завершается), поэтому этот цикл "while ( (PPU_STATUS & b0100_0000) == 0);" не выполняется, если перед NMI был Sprite 0 Hit. Попробуйте добавить перед этим циклом: while ( (PPU_STATUS & b0100_0000) != 0);. |
| Howard Phillips:
--- Цитата: Sharpnull от 11 Май 2024, 17:41:15 ---Howard Phillips, бит Sprite 0 Hit очищается на "dot 1 of the pre-render line" (в Mesen это Scanline: -1, после 260), а выход из NMI происходит раньше (для NTSC начинается со scanline 241 и почти сразу завершается), поэтому этот цикл "while ( (PPU_STATUS & b0100_0000) == 0);" не выполняется, если перед NMI был Sprite 0 Hit. Попробуйте добавить перед этим циклом: while ( (PPU_STATUS & b0100_0000) != 0);. --- Конец цитаты --- Sharpnull, вы гений, спасибо. Заработало с первого раза. Не перестаю удивляться подобным подводным камням при разработке под NES. А нет идей по поводу потемнения некоторых цветов при изменении положения скроллинга? Или это возможно как-то пиксели смещаются немного и получается такой эффект из-за наложения. Полосы, которые нарисованы прозрачными пикселями становятся толще, но скринами это поймать не смог и некоторые цветные пиксели тоже как-будто тоже темнеют немного. Возможно как у меня скроллинг на 1 пиксель как-то дергается и смазываются цвета? Скринами не смог поймать этот момент, что показать, видно только в динамике игры (на видео тоже почему-то не видно эффекта). |
| Sharpnull:
--- Цитата: Howard Phillips от 13 Май 2024, 05:25:57 ---Скринами не смог поймать этот момент, что показать, видно только в динамике игры (на видео тоже почему-то не видно эффекта). --- Конец цитаты --- Это дурацкие ЖК экраны, пиксели же не сразу меняют правильный цвет, но на видео должно быть видно во время воспроизведения. Свою матрицу можете протестировать https://www.testufo.com/ghosting. Запустите Ninja Gaiden, там сразу же забор при движении становится темнее из-за паттерна "2px тёмных, 2px серых". Может зависит не только от скорости матрицы, но и типа, у меня IPS в старом дешёвом мониторе. |
| Howard Phillips:
Sharpnull, Проверил на Ninja Gaiden свой монитор, действительно забор темнее при скроллинге становится на моем мониторе. Эффект похож. Потом надо проверить как это выглядит на ЭЛТ телеке и реальной консоли. |
| Навигация |
| Главная страница сообщений |
| Предыдущая страница |