Автор Тема: Хранение клона OAM таблицы в PRG-ROM NES  (Прочитано 1459 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Sharpnull

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #30 : 01 Март 2023, 15:02:55 »
так как в левом верхнем углу выводится фон из первой страницы, хотя для фонов у меня выбрана вторая страница все время выполнения программы
Вы писали, что у вас в левом верхнем углу спрайт, а не фон, и страницы наоборот. Ладно, запускайте Mesen и смотрите, там просто понять откуда и что берётся. Скорее всего у вас какие-то спрайты (настоящие железные) имеют данные 00 xx xx 00 по адресам CPU $0200-02FF и они туда съехали, нужно у неиспользованных спрайтов ставить координату Y (готовое значение) от EF до FF.
Какое-то шаманство, очень напрягают такие моменты. почти одинаковый код, а поведение в чем-то принциально отличается.
В прошлый раз код был правильный в обоих случаях в том месте (стоило проверить в эмуляторе). Проблема может быть в другом месте. Проблемы появляющиеся только из-за порядка кода или аналогичного кода могут быть из-за забытого где-то volatile и происходит оптимизация или что-то такое. Возможно листинг, который вы показывали, изменяется из-за последующих оптимизаций. Для проверки такого нужно отключить оптимизацию насколько возможно. У вас это убрать флаги -O и может что-то ещё.
UPD:
Неужели это компилятор такой проблемный?
cc65 обновляется постоянно (последнее изменение Feb 26, 2023), можно попробовать новую версию, но может получится, что проблема будет просто отсрочена и вылезет при другом коде.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #31 : 01 Март 2023, 19:01:53 »
Цитата
Вы писали, что у вас в левом верхнем углу спрайт, а не фон, и страницы наоборот. Ладно, запускайте Mesen и смотрите, там просто понять откуда и что берётся. Скорее всего у вас какие-то спрайты (настоящие железные) имеют данные 00 xx xx 00 по адресам CPU $0200-02FF и они туда съехали, нужно у неиспользованных спрайтов ставить координату Y (готовое значение) от EF до FF.
Тут я еще не привык к терминологии. Я изучал разработку под NES по разрозненным статьям, а там немного разная терминология везде (тем более многие статьи переводные).
По спрайтами я подразумеваю любые атомарные блоки 8х8 из CHR.
И вы оказались правы о природе артефакта в левом верхнем углу. Я перед запуском все спрайты инициализировал пустым спрайтом и вывел за экран по адресу Y = 0xFF и артефакт пропал. Спасибо, что помогаете.

PS. Попробовал отключить оптимизацию (был ключ -Oi) и без оптимизации баг пропал.
« Последнее редактирование: 01 Март 2023, 20:19:13 от Howard Phillips »

Оффлайн Sharpnull

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #32 : 02 Март 2023, 03:18:21 »
По спрайтами я подразумеваю любые атомарные блоки 8х8 из CHR.
Не представляю кто такое мог написать. Неразделимый блок графики всегда был "тайлом" (tile), обычно 8x8 px. "Спрайт" (sprite) состоит из тайлов, в отношении старых систем это именно аппаратные спрайты (бывало без них, печальное зрелище), у NES они 8x8 или 8x16 px, а у Mega Drive можно большего размера.
Я тоже не очень хорошо знаю терминологию, например "фоном" (background) можно назвать всю область без спрайтов, который включает в себя один или больше nametable, причём он включает attribute table, но как назвать область только из индексов тайлов. Nametable похож на более обширный термин - tilemap, а CHR получается tileset, при этом pattern tables это область PPU $0000-1FFF, а не сама графика (?).
PS. Попробовал отключить оптимизацию (был ключ -Oi) и без оптимизации баг пропал.
Как и говорил, вероятно удалилось что-то важное.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #33 : 31 Март 2023, 00:02:57 »
Здравствуйте. Снова я возвращаюсь с дурацким вопросом, но сам я решить проблему не смог, потому жду помощи.
У меня хранятся палитры вот в таком видео:
const unsigned char PALETTE[]={
0x0F, 0x17, 0x28, 0x38,  0x0F, 0x38, 0x29, 0x0A,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, // Палитры фонов
0x0F, 0x17, 0x28, 0x38,  0x0A, 0x13, 0x23, 0x33,  0x0F, 0x07, 0x27, 0x39,  0x00, 0x00, 0x00, 0x00}; // Палитры спрайтов
// А вот функция загрузки палитр
void Load_Palette (void) {
PPU_ADDRESS = 0x3f;
PPU_ADDRESS = 0x00;
for( i = 0; i < sizeof(PALETTE); ++i ){
PPU_DATA = PALETTE[i];
}
}
Нулевой элемент массива PALETTE должен задавать цвет фона для нулевой палитры фона по адресу 0x3f00. Но у меня почему-то этот адрес никак не влияет на цвет фона, а цвет фона меняется нулевым байтом палитры спрайтов -по адресу $3F10.
Но нулевые цвета палитр спрайтов прозрачные и записанные там коды ни на что не должны влиять. Я снова что-то не понимаю? Почему запись в 0x3f00 не меняет цвет фона, а запись в $3F10 меняет универсальный фон?
Оптимизитор пробовал отключать - это не помогло.

PS: Демку игры я доделал, щас уже занимаюсь расширением скелета игры, добавляю фичи.

Оффлайн Sharpnull

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #34 : 31 Март 2023, 00:54:52 »
Но нулевые цвета палитр спрайтов прозрачные и записанные там коды ни на что не должны влиять. Я снова что-то не понимаю? Почему запись в 0x3f00 не меняет цвет фона, а запись в $3F10 меняет универсальный фон?
Как написано здесь https://www.nesdev.org/wiki/PPU_palettes, адреса $3F10/$3F14/$3F18/$3F1C это зеркала $3F00/$3F04/$3F08/$3F0C, поэтому изменение $3F10 равносильно изменению $3F00, а вот $3F04/$3F08/$3F0C и $3F14/$3F18/$3F1C в обычных условиях не влияют на картинку.
Вы уверены, что запись в $3F00 не меняет цвет? Т. е. вы должны были убрать запись в $3F10. Ещё перед записью в PPU нужно делать BIT $2002 (или LDA $2002) (UPD: Это вроде не нужно, если ранее такое было).

Какое совпадение, что 29 марта 2023 добавили информацию об очередном баге в железе: https://www.nesdev.org/wiki/PPU_registers#Palette_corruption. (UPD4: Неправильно сначала написал) Оказывается после записи палитры нужно сделать так (как в некоторых играх):
  lda #$3F
  sta PPUADDR
  lda #0
  sta PPUADDR
  sta PPUADDR
  sta PPUADDR
Например в Battle City - CPU $D50E:

Я что-то похожее видел в коде лицензионных игр, кучу каких-то странных записей типа этого, но в эмуляторе проблемы не будет.
UPD2: Например, во время VBlank в Teenage Mutant Ninja Turtles II - The Arcade Game (U) [!] по адресу CPU $F998:
Teenage Mutant Ninja Turtles III - The Manhattan Project (U) [!] - CPU $F564:
UPD3: В обоих случаях дальше нет записи в $2007.
UPD5: Почитал обсуждение, странные записи в TMNTII и TMNTIII нужны для прерываний в MMC3: https://forums.nesdev.org/viewtopic.php?p=281099#p281099. А про Palette corruption не понял, если записывать по 16 байт в палитру (там пишут про $3F10-3F1F), то вроде исправление не нужно и разработчики игр вставили на основе советов от Nintendo на всякий случай, чтобы проверить нужны старые ревизии, всё плохо.
« Последнее редактирование: 31 Март 2023, 01:40:08 от Sharpnull »

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #35 : 31 Март 2023, 19:54:26 »
Вот год функции загрузки палитры после компиляции
; void __near__ Load_Palette (void)
; ---------------------------------------------------------------

.segment "CODE"

.proc _Load_Palette: near

.segment "CODE"

;
; PPU_ADDRESS = 0x3f;
;
lda     #$3F
sta     $2006
;
; PPU_ADDRESS = 0x00;
;
lda     #$00
sta     $2006
;
; for( i = 0; i < sizeof(PALETTE); ++i ){
;
sta     _i
L0007: lda     _i
cmp     #$20
bcs     L0003
;
; PPU_DATA = PALETTE[i];
;
ldy     _i
lda     _PALETTE,y
sta     $2007
;
; for( i = 0; i < sizeof(PALETTE); ++i ){
;
inc     _i
jmp     L0007
;
; }
;
L0003: rts

.endproc
А вот массив:
const unsigned char PALETTE[]={
0x0A, 0x17, 0x28, 0x38,  0x0F, 0x38, 0x29, 0x0A,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, // Палитры фонов
0x0F, 0x31, 0x21, 0x02,  0x0A, 0x13, 0x23, 0x33,  0x0F, 0x07, 0x27, 0x39,  0x00, 0x00, 0x00, 0x00}; // Палитры спрайтов
0x0A или не записывается или записывается, но не меняет цвет. А если после вызова функции я меняю цвет по адресу $3F00 вот так:
All_Off(); // turn off screen

Reset_Scroll ();
reset_map ();
reset_OAM ();
Load_Palette();

PPU_ADDRESS = 0x3f;
PPU_ADDRESS = 0x00;
PPU_DATA = PALETTE [0];
All_On ();

То цвет меняется нормально. Ассемблерный код нормальный, должен цвет меняться, но как-то неправильно функция работает судя по всему. Странно это все.


Оффлайн Sharpnull

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #36 : 31 Март 2023, 22:34:40 »
А вот массив
Я же писал, что $3F00 в который вы записываете 0A и $3F10 (у вас = 0F) - зеркала, это значит, что изменение $3F00 изменяет одновременно $3F10 и наоборот. Поэтому ставьте в массиве для $3F00 и $3F10 одинаковые значения. UPD: Не понял почему раньше вы писали про нулевой байт, ну попробуйте 0-й цвет для каждой палитры (4 байта) менять на один и тот же цвет, как обычно и делают.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #37 : 01 Апрель 2023, 00:13:15 »
Точно, я понял зеркала, но до конца не довел мысль в голове, что записывая весь массив, я два раза меня цвет фона. Спасибо за пояснение.

Под нулевым байтом я подразумевал нулевой байт палитр, неудачно сформулировал.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #38 : 16 Апрель 2023, 17:43:59 »
И снова я с дурацкими вопросами. Игра приближается к относительно законченной версии и начал немного убирать баги. Вопрос следующий.
Когда мне нужно изменить большое количество тайлов фона, я отключаю экран, редактирую фон и включаю экран обратно.
Но в момент включения экрана отрисовка первого кадра  начинается не с левого верхнего угла как положено, а из случайной части экрана и получается что-то вроде рывка всего фона, но в следующем кадре уже все нормально. Такая тряска экрана выглядит некрасиво, хотелось бы чтоб никаких рывков фона не было.
Вот код включения и выключения экрана:
void All_Off (void) {
PPU_CTRL = 0;
PPU_MASK = 0;
}


void All_On (void) {
PPU_CTRL = 0x90; /* screen is on, NMI on 0b 1001 0000
                                                |||| ||||
                                                |||| ||++- Выбор базовой таблицы имен
                                                |||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
                                                |||| |+--- VRAM address increment per CPU read/write of PPUDATA
                                                |||| |     (0: add 1, going across; 1: add 32, going down)
                                                |||| +---- Sprite pattern table address for 8x8 sprites
                                                ||||       (0: $0000; 1: $1000; ignored in 8x16 mode)
                                                |||+------ Background pattern table address (0: $0000; 1: $1000)
                                                ||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels – see PPU OAM#Byte 1)
                                                |+-------- PPU master/slave select
                                                |          (0: read backdrop from EXT pins; 1: output color on EXT pins)
                                                +--------- Generate an NMI at the start of the
                                                        vertical blanking interval (0: off; 1: on)
                     */

PPU_MASK = 0x1E; // 0b 0001 1110
                           // | ||||
                           // | |||+ - включает режим в оттенках серого
                           // | ||+ - включает показ фона в крайнем левом стобце
                           // | |+ - включает показ спрайтов в крайнем левом столбце
                           // | + - включает показ фона
                           // + - включает показ спрайтов
}

Пробовал обнулять PPU_ADDRESS перед включением, но это не помогло.
Еще видел совет, что надо ждать прерывания конца отрисовки кадра перед включением экрана. Но при выключенном экране оно не работает.
Как мне корректно включать экран? Спасибо.

Оффлайн Sharpnull

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #39 : 16 Апрель 2023, 18:46:10 »
Еще видел совет, что надо ждать прерывания конца отрисовки кадра перед включением экрана. Но при выключенном экране оно не работает.
Да, включать экран нужно в VBlank. Для этого нужно синхронизировать код. Можно не отключать генерацию NMI, когда экран выключен, в этом случае можно выделить байт в RAM для записи в PPUCTRL, тогда вне обработчика NMI вы изменяете байт в RAM, а в NMI будет реальная запись в PPUCTRL. Такие переменные/адреса называются "shadow" что-то там (UPD: или Pseudo-registers как в FDS BIOS: https://www.nesdev.org/wiki/FDS_BIOS). Другой вариант с включённым NMI, это ожидать VBlank, когда в NMI происходит установка или инкремент значения в RAM, которое в основном потоке проверяется на изменение, наверно у вас такой вариант и он не работает из-за откл. NMI. Остаётся ждать VBlank как после запуска игры, что не рекомендуется если у вас вкл. NMI:
@vblankwait:
    BIT $2002 ; или LDA $2002
    BPL @vblankwait
UPD2: Отключать экран тоже нужно в VBlank, иначе будет чёрный экран до конца кадра с момента откл. экрана.
Под нулевым байтом я подразумевал нулевой байт палитр, неудачно сформулировал.
Вы нормально написали, я тогда не понял, сам иногда ломаю голову когда нужно объяснить "индексы", "цвета", "индекс цвета", "индекс цвета в палитре" и т. п.
« Последнее редактирование: 16 Апрель 2023, 20:34:39 от Sharpnull »

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #40 : 16 Апрель 2023, 22:12:28 »
Спасибо, сделал прерывания 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

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #41 : 16 Апрель 2023, 22:45:53 »
Но во время отключения экрана видно, что экран успевает "моргнуть", т.е. почернеть на один кадр, а потом картинка возвращается.
Здесь мне не понятно что происходит, что вызывается и когда, как я понял: вызывается 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

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #42 : 16 Апрель 2023, 23:15:50 »
Вот пример вызова включения и выключения экрана:
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

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #43 : 16 Апрель 2023, 23:48:16 »
Судя по коду, перед вызовом 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);
После вызова  All_Off() экран чернеет и  картинка возвращается только после вызова All_On().
Но вы писали про моргание.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #44 : 17 Апрель 2023, 00:30:13 »
Да, вы правы, у меня была странноватая логика ожидания прерывания. Это все остатки кода на основе которого я изучал разработку под 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

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #45 : 17 Апрель 2023, 01:28:19 »
Переписал функцию ожидания прерывания на ассемблере вот так:
Ожидать через lda $2002 bpl можно только с отключенным NMI, иначе может быть пропуск кадра. С вкл. NMI напишите _Wait_Vblank так:
  LDA Frame_Count
@loop:
  CMP Frame_Count
  BEQ @loop
  RTS
А вообще после отключения отображения спрайтов и фона, должны ли они оставаться на экране?
После PPUMASK = 0 будет весь фон одного цвета фона (у вас видимо чёрный). Если хотите "заморозить" картинку, то придётся без откл. экрана записывать в VBlank в течение нескольких кадров, но т. к. вы меняете фон, это будет заметно, остаётся делать двойную буферизацию: отображать один Nametable и изменять другой за пределами экрана, а потом в VBlank менять скрытый на отображаемый Nametable через Scroll ($2005) и Base nametable address ($2000), но вряд ли вы так будете делать.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #46 : 17 Апрель 2023, 14:26:44 »
Заменил функцию ожидания на ваш код, но визуально разницу не заметил в работе игры. Но стабильность не помешает, я согласен.
А про борьбу с черным экраном я так и думал, что тут только или буферизация или фон редактировать маленькими кусочками за несколько срабатываний NMI. Самые критичные моменты моменты я потом перепишу без отключения экрана.
Сейчас все вроде бы работает как надо. Можно дальше двигаться к релизу.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #47 : 11 Май 2024, 16:39:50 »
Здравствуйте. При разработке столкнулся с очередной проблемой.
Решил сделать раздельный скроллинг для вывода интерфейса вверху экрана (выставляю позицию камеры в 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

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #48 : 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);.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Re: Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #49 : 13 Май 2024, 05:25:57 »
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

  • Пользователь
  • Сообщений: 5073
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #50 : 13 Май 2024, 06:17:43 »
Скринами не смог поймать этот момент, что показать, видно только в динамике игры (на видео тоже почему-то не видно эффекта).
Это дурацкие ЖК экраны, пиксели же не сразу меняют правильный цвет, но на видео должно быть видно во время воспроизведения. Свою матрицу можете протестировать https://www.testufo.com/ghosting. Запустите Ninja Gaiden, там сразу же забор при движении становится темнее из-за паттерна "2px тёмных, 2px серых". Может зависит не только от скорости матрицы, но и типа, у меня IPS в старом дешёвом мониторе.

Оффлайн Howard Phillips

  • Пользователь
  • Сообщений: 32
    • Просмотр профиля
Хранение клона OAM таблицы в PRG-ROM NES
« Ответ #51 : 13 Май 2024, 12:58:53 »
Sharpnull, Проверил на Ninja Gaiden свой монитор, действительно забор темнее при скроллинге становится на моем мониторе. Эффект похож. Потом надо проверить как это выглядит на ЭЛТ телеке и реальной консоли.