Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Сообщения - Howard Phillips

Страницы: [1] 2 Далее
1
Sharpnull, Проверил на Ninja Gaiden свой монитор, действительно забор темнее при скроллинге становится на моем мониторе. Эффект похож. Потом надо проверить как это выглядит на ЭЛТ телеке и реальной консоли.

2
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 пиксель как-то дергается и смазываются цвета?
Скринами не смог поймать этот момент, что показать, видно только в динамике игры (на видео тоже почему-то не видно эффекта).

3
Здравствуйте. При разработке столкнулся с очередной проблемой.
Решил сделать раздельный скроллинг для вывода интерфейса вверху экрана (выставляю позицию камеры в 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-й кадр интерфейс выводится в неправильное положение скроллинга и получается мерцание.
Еще есть у меня баг со скроллигом, когда движется камера, некоторые цвета заметно темнеют. Возможно это связанные проблемы.
Прикрепляю скрин на котором я поймал правильный вывод интерфейса. Четко видно, что вывод интерфейса работает через кадр. Я из кода все лишнее выкинул и непонятно, что может ломать скроллинг. Очень надеюсь на помощь, так как спрайтами меню выводить не хочется (они почти закончились).

4
Картриджи / диски / Re: Mega2560 Shield NES Dumper/Writer
« : 30 Ноябрь 2023, 10:32:35 »
Здравствуйте. Может у кого-то остались платы шилда?

5
Заменил функцию ожидания на ваш код, но визуально разницу не заметил в работе игры. Но стабильность не помешает, я согласен.
А про борьбу с черным экраном я так и думал, что тут только или буферизация или фон редактировать маленькими кусочками за несколько срабатываний NMI. Самые критичные моменты моменты я потом перепишу без отключения экрана.
Сейчас все вроде бы работает как надо. Можно дальше двигаться к релизу.

6
Да, вы правы, у меня была странноватая логика ожидания прерывания. Это все остатки кода на основе которого я изучал разработку под 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 в итоге я вообще выкинул из программы, мне тоже этот костыль не нравился изначально, но раньше было лень рефакторить.

7
Вот пример вызова включения и выключения экрана:
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().

8
Спасибо, сделал прерывания 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 и посмотреть сколько пустых страниц в файле. Очень просто и наглядно.

9
И снова я с дурацкими вопросами. Игра приближается к относительно законченной версии и начал немного убирать баги. Вопрос следующий.
Когда мне нужно изменить большое количество тайлов фона, я отключаю экран, редактирую фон и включаю экран обратно.
Но в момент включения экрана отрисовка первого кадра  начинается не с левого верхнего угла как положено, а из случайной части экрана и получается что-то вроде рывка всего фона, но в следующем кадре уже все нормально. Такая тряска экрана выглядит некрасиво, хотелось бы чтоб никаких рывков фона не было.
Вот код включения и выключения экрана:
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 перед включением, но это не помогло.
Еще видел совет, что надо ждать прерывания конца отрисовки кадра перед включением экрана. Но при выключенном экране оно не работает.
Как мне корректно включать экран? Спасибо.

10
Точно, я понял зеркала, но до конца не довел мысль в голове, что записывая весь массив, я два раза меня цвет фона. Спасибо за пояснение.

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

11
Вот год функции загрузки палитры после компиляции
; 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 ();

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


12
Здравствуйте. Снова я возвращаюсь с дурацким вопросом, но сам я решить проблему не смог, потому жду помощи.
У меня хранятся палитры вот в таком видео:
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: Демку игры я доделал, щас уже занимаюсь расширением скелета игры, добавляю фичи.

13
Цитата
Вы писали, что у вас в левом верхнем углу спрайт, а не фон, и страницы наоборот. Ладно, запускайте Mesen и смотрите, там просто понять откуда и что берётся. Скорее всего у вас какие-то спрайты (настоящие железные) имеют данные 00 xx xx 00 по адресам CPU $0200-02FF и они туда съехали, нужно у неиспользованных спрайтов ставить координату Y (готовое значение) от EF до FF.
Тут я еще не привык к терминологии. Я изучал разработку под NES по разрозненным статьям, а там немного разная терминология везде (тем более многие статьи переводные).
По спрайтами я подразумеваю любые атомарные блоки 8х8 из CHR.
И вы оказались правы о природе артефакта в левом верхнем углу. Я перед запуском все спрайты инициализировал пустым спрайтом и вывел за экран по адресу Y = 0xFF и артефакт пропал. Спасибо, что помогаете.

PS. Попробовал отключить оптимизацию (был ключ -Oi) и без оптимизации баг пропал.

14
Sharpnull,
Цитата
Не понял. У вас спрайты со 2-й страницы CHR и нулевой спрайт из 2-й страницы как должно быть, а фон не важно с какой страницы, они могут быть с одной страницы. Когда пишите "адрес", лучше уточняйте как-то так: CPU $xxxx (или RAM $xxxx, что совпадает для RAM с CPU), PPU $xxxx, PRG $xxxxxx (относительно PRG ROM без заголовка iNES), CHR $xxxxxx (относительно CHR ROM без заголовка iNES), File $xxxxxx (относительно начала файла, т. е. с заголовком).
Я понимаю, что спрайты и фон могут быть с одной страницы в один момент времени. Я уточнил, что у меня настроены они на разные страницы, так как в левом верхнем углу выводится фон из первой страницы, хотя для фонов у меня выбрана вторая страница все время выполнения программы. Страницу для фонов я не переключаю.
Цитата
weapons [p_selected_mech->l_arm_weapon_index_].name_
Снова столкнулся с багом при таком обращении к элементу массива. Нашел закономерность, что графика падает, если такое обращение было сделано при выключенном экране, а если обращаться при включенном экране, то все нормально.

И если разворачиваю цикл вывода в этой функции:
void draw_weapon_submenu (void) {
    // Указываем адрес первого симовла в таблицу имен
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR; 
    PPU_ADDRESS = submenu_items_addr [0];
    // Берем адрес начала массива
    p_text = weapons [p_selected_mech->r_arm_weapon_index_].name_; 
    // Выводим оружие правой руки
    for (i = 0; i < PART_NAME_SIZE; ++i) {
PPU_DATA = p_text [i];
}
   
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR;
    PPU_ADDRESS = submenu_items_addr [1];
    // Если заменить эту строку на обращение через указатель, графика падает
    p_text = weapons [mechs [index_selected_mech].l_arm_weapon_index_].name_;
    // Выводим оружие левой руки руки
    for (i = 0; i < PART_NAME_SIZE; ++i) {
PPU_DATA = p_text [i];
}
}

Вот так:
void draw_weapon_submenu (void) {
    // Указываем адрес первого симовла в таблицу имен
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR; 
    PPU_ADDRESS = submenu_items_addr [0];
    // Берем адрес начала массива
    p_text = weapons [p_selected_mech->r_arm_weapon_index_].name_; 
    // Выводим оружие правой руки
    PPU_DATA = p_text [0];
    PPU_DATA = p_text [1];
    PPU_DATA = p_text [2];
    PPU_DATA = p_text [3];
    PPU_DATA = p_text [4];
    PPU_DATA = p_text [5];
    PPU_DATA = p_text [6];
    PPU_DATA = p_text [7];
    PPU_DATA = p_text [8];
    PPU_DATA = p_text [9];
   
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR;
    PPU_ADDRESS = submenu_items_addr [1];
    // Если заменить эту строку на обращение через указатель, графика падает
    p_text = weapons [mechs [index_selected_mech].l_arm_weapon_index_].name_;
    // Выводим оружие левой руки руки
    for (i = 0; i < PART_NAME_SIZE; ++i) {
PPU_DATA = p_text [i];
}
}
Графика тоже падает. Какое-то шаманство, очень напрягают такие моменты. почти одинаковый код, а поведение в чем-то принциально отличается. Неужели это компилятор такой проблемный?

15
Sharpnull, давно у меня был вопрос по верхним 8 пикселям.
Почему в регионе PAL по адресу 2000 (и на всю верхнюю строку)  спрайты фона выводятся нормально, а спрайты из OAM не выводятся (исчезает часть метатайла, которая туда попадает)?
И еще в левом верхнем углу у меня отображается нулевой спрайт (из адреса 0х00) из второй страницы CHR, хотя фоны у меня настроены на первую страницу CHR, а спрайты для ОАМ вывожу со второй страницы CHR.

16
Sharpnull,
Цитата
Не только NameTable и Attribute Table, и графику, если CHR RAM. Это во время вкл. экрана. Мотивация такая: VBlank короткий, можно во время рендера/рисования (вне VBlank) высчитать что нужно заменить, а в VBlank только запись в PPU. Это не всегда нужно и может только отнимать время, например, для палитры могут отвести своё место в RAM, вне VBlank заполнить RAM цветами и поставить флаг, в VBlank проверить флаг и записать в PPU, если нужно.
Значит весь код, который не обращается к PPU, корректно отрабатывает вне VBlank? Я сейчас все расчеты делаю только во время VBlank или выключаю экран, если код долгий.

Цитата
Из простых способов: добавить в конце всех данных и кода какую-нибудь уникальную строчку и написать скрипт, который будет считать. Есть будет переполнение, вы всё равно узнаете. Можете смотреть в эмуляторе или HEX-редакторе будет видно.
Я надеялся, что есть какой-то штатный способ. Значит буду просто ждать переполнения :). Интересный, конечно, опыт получается с разработкой под NES, до этого больших проектов на си не писал и тем более под непопулярные компиляторы. Еще чувствую, что будут веселые пляски с созданием картриджа, наверняка куча граблей вылезет.

17
Sharpnull, спасибо, что  глянули файлы.  Функцию вызывал при выключенном экране. Мб у меня какое-то неудачное для эмулятора сочетание команд получается именно в том месте, хотя я использовал подобные вызовы в других местах - там все нормально отрабатывает. Пусть пока будет вариант менее оптимальный, работает и ладно.

Цитата
Наверно сложно сделать как обычно в играх для NES: на каждый атрибут/поле фиксированный массив и обращение просто как LDX #индекс_объекта | LDA начало_поля,X.
Сделать несложно, просто тогда код получается плохо читаемый, придется держать в уме какой элемент массива, что обозначает. Можно, как вариант, сделать именованные индексы через enum'ы, но не уверен, что это красиво будет. В следующем проекте мб попробую избавиться от структур.

Цитата
Вообще принято записывать в буфер (в RAM, где адреса и данные для записи), откуда в VBlank происходит запись в PPU.
Т.е. обычно выделяют память под клон таблицы имен и периодически выгружают его в PPU?

И еще есть дурацкий вопрос по сс65 компилятору. Как узнать сколько ROM я уже занял, я гуглил, но ничего не смог найти. В списке ключей ничего такого не смог найти.

Офтоп: Попробовал в опере растягивать форму ввода - тоже самое, ничего не происходит. Везет мне на странности.

18
Поймал очень странный баг сейчас. И снова у меня нет идей в чем проблема. Есть такая функция:
void draw_weapon_submenu (void) {
    // Указываем адрес первого симовла в таблицу имен
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR; 
    PPU_ADDRESS = submenu_items_addr [0];
    // Берем адрес начала массива
    p_text = weapons [p_selected_mech->r_arm_weapon_index_].name_; 
    // Выводим оружие правой руки
    for (i = 0; i < PART_NAME_SIZE; ++i) {
PPU_DATA = p_text [i];
}
   
    PPU_ADDRESS = HIGH_BYTE_MENU_ADDR;
    PPU_ADDRESS = submenu_items_addr [1];
    p_text = weapons [mechs [index_selected_mech].l_arm_weapon_index_].name_;
    // Выводим оружие левой руки руки
    for (i = 0; i < PART_NAME_SIZE; ++i) {
PPU_DATA = p_text [i];
}
}
Если заменяю
p_text = weapons [mechs [index_selected_mech].l_arm_weapon_index_].name_;
на любой другой способ обращения к элементу массива weapons  по индексу (например, weapons
  • или weapons [p_selected_mech->r_arm_weapon_index_],

То игра падает. Причем первое оружие выводится нормально через указатель, а второе выводится нормально только через обращение к массиву мехов.
Прикрепляю рабочий (первый) и нерабочий вариант сгенерированного кода в виде файлов. Такое ощущение, что разные способы обращению ломают режим вывода, но даже не могу представить как оно может влиять.
PS: Если вызывать функцию в других местах, то функция работает в любых вариантах.

19
Sharpnull, значит я как-то некорректно тестировал. Но обратно менять уже не буду.
Я тут посмотрел другие сгенерированные компилятором функции из Си кода. Там вообще ужас, где происходит обращение к полям структур (я думал там адресация примерно как с массивами, если поля одинаковой размерности, но нет), так что в моем случае гнаться за хорошей оптимизацией особо смысла нет.
Если я решу заняться полноценной оптимизацией, придется выкидывать все структуры (а их много, ибо приходится хранить много  параметров игровых объектов), а это равноценно переписыванию половины проекта.
Изначально я старался сделать хорошую архитектуру проекта, насколько это позволяет Си и мои навыки программиста. Щас уже, конечно, проект разросся и не так красиво все выглядит, но если демка игры зайдет, мб буду рефакторить все. Раньше времени закапываться в перфекционизм не хочется.

20
Sharpnull, хорошая мысль с выкидываем Temp. Вот сгенерированный код без volatile:
; void __near__ Get_Input2 (void)
; ---------------------------------------------------------------

.segment "CODE"

.proc _Get_Input2: near

.segment "CODE"

;
; JOYPAD1 = 1;
;
lda     #$01
sta     $4016
;
; JOYPAD1 = 0;
;
lda     #$00
sta     $4016
;
; joypad1old = joypad1;
;
lda     _joypad1
sta     _joypad1old
;
; joypad1 = JOYPAD1;      // A
;
lda     $4016
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // B
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Select
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Start
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Up
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Down
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Left
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; joypad1 <<= 1;
;
asl     a
sta     _joypad1
;
; joypad1 |= JOYPAD1 & 1; // Right
;
lda     $4016
and     #$01
ora     _joypad1
sta     _joypad1
;
; }
;
rts
Работает одинаково и с volatile и без него.
Офтоп: У меня гугл хром, странно, что у меня не работает растягивание окна. Очень неудобно так оформлять ответ.

21
Sharpnull, вот полный код функции сгенерированный:
.segment "CODE"

.proc _Get_Input2: near

.segment "CODE"
;
; JOYPAD1 = 1;
;
lda     #$01
sta     $4016
;
; JOYPAD1 = 0;
;
lda     #$00
sta     $4016
;
; temp = JOYPAD1;      // A
;
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // B
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Select
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Start
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Up
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Down
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Left
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
;
; temp |= JOYPAD1 & 1; // Right
;
lda     $4016
and     #$01
ora     _temp
sta     _temp
;
; joypad1old = joypad1;
;
lda     _joypad1
sta     _joypad1old
;
; joypad1 = temp;
;
lda     _temp
sta     _joypad1
;
; }
;
rts
Офтоп. А почему у меня не растягивается поле для написания сообщения? Хотя двустороння стрелка появляется. Это у меня проблема или глюк форума?

22
Sharpnull,  точно. Спасибо за быстрый ответ. Я забыл, что в 0x4016 не только D0 хранится и почему-то думал, что сдвиг до 7 делается за одну команду. Ваш код заработал.
Я решил переписать на Си функцию чтения для большего единообразия кода и лучшей читаемости. Стараюсь минимизировать ассемблер. И у меня игра пошаговая, там нет борьбы за каждый такт. У меня основа движка написана уже, почти все написано на Си (Распаковка RLE только на асме теперь осталась), скорости пока хватает.
А по поводу оптимизации. Со сдвигами на один бит, получается вполне нормальный код. Вот начало функции после компиляции:
;
; JOYPAD1 = 1;
;
lda     #$01
sta     $4016
;
; JOYPAD1 = 0;
;
lda     #$00
sta     $4016
;
; temp = JOYPAD1;      // A
;
sta     _temp
;
; temp <<= 1;
;
asl     a
sta     _temp
Разве плохо? Руками было бы тоже самое. Что тут можно оптимизировать?

23
Здравствуйте. Я продолжаю делать свою игру под NES.
У меня есть функция опроса кнопок, она написана на асме и работает нормально. Но я решил переписать ее на Си. Вышло вот так:
void Get_Input2 () {
// Сторобируем геймпад
// Адрес геймпада - 0x4016
JOYPAD1 = 1;
JOYPAD1 = 0;
    temp = 0;
// Считываем значения всех кнопок
   
    temp |= JOYPAD1 <<  7; // Чтение А
    temp |= JOYPAD1 <<  6; // Чтение В
    temp |= JOYPAD1 <<  5; // Чтение Select
    temp |= JOYPAD1 <<  4; // Чтение Start
    temp |= JOYPAD1 <<  3; // Чтение Вверх
    temp |= JOYPAD1 <<  2; // Чтение Вниз
    temp |= JOYPAD1 <<  1; // Чтение Влево
    temp |= JOYPAD1 <<  0; // Чтение Вправо

    // Сохраняем предыдущие нажатия кнопок
    joypad1old = joypad1;
    // Сохравняем новое нажатие
joypad1 = temp;
}
Старший бит temp - это А, следующий это В и тд.
JOYPAD1 - дефайн 0x4016
Все переменные unsigned char
Логика чтения  простейшая, но почему-то кнопки А и Б не считываются, а остальные считываются нормально. У меня вообще нет идей почему так. Явно какая-то глупая ошибка или я упускаю какой-то важный момент механики работы геймпадов.
Может слишком быстро я пытаюсь считать все кнопки? Что я делаю не так?
Компилятор все тот же сс65.
Спасибо.

24
Я не думаю, я знаю. Это повсеместно встречалось в старых играх. Не знаю чем раньше разрабы руководствовались.
https://www.youtube.com/watch?v=xkiHCURKqVU

Добавлено позже:
Зная, что RE2 для PS2 это посредственный порт, проверил оригинальную GC версию. И нет у оригинала этого бреда с мёртвыми зонами. Это ещё один подарочек от отдела портирования.
Вот это бред действительно. И можно как-то обойти эти программные мертвые зоны в играх? В видео автор менял значения каких-то переменных, но я не понял где именно.

25
У меня из этого набора игр под рукой есть только RE4 и в ней тракторное управление заложено разработчиками. Эмуляция стиков тут ни при чём.

Добавлено позже:
Вот тут grooomy пытается решать подобное в играх для PS1. Можете попробовать проделать аналогичное и для PS2 игр.
В RE4 в крайнем положении стика быстро перемещение прицела, а в не совсем крайнем прицел двигается медленно. Значит так все-таки есть какая-то вариативность скорость движения прицела. Вы думаете мертвые зоны прописаны в самой игре?

26
Ну во всех вы проверить не могли, поэтому назовите парочку примеров, чтоб было над чем подумать.
RE4, Castlevania - Lament of Innocence, Civil War: A Nation Divided, Darkwatch

27
Здравствуйте. Не получается отключить мертвые зоны в pcsx2.
Настройки плагина геймпада в прикрепленном файле. После установки мертвых зон в ноль, в играх они остаются. Проверено на разных геймпадах.
При том, что в режиме проверки кнопок, эмулятор показывает даже минимальные отклонения стиков, а в играх мертвая зона на половину амплитуды стика. Такая проблема во всех играх.

28
Здравствуйте. Не получается отключить мертвые зоны в pcsx2.
Настройки плагина геймпада в прикрепленном файле. После установки мертвых зон в ноль, в играх они остаются. Проверено на разных геймпадах.
При том, что в режиме проверка кнопок, эмулятор показывает минимальные отклонения стиков, а в играх мертвая зона на половину амплитуды стика. Такая проблема во всех играх. [ Вложение повреждено или не существует ]
Как еще можно отключить мертвую зону? Из-за нее невозможно играть в шутеры.

29
nonamezerox, спасибо за наводку.
Посмотрел презентацию от разработчика, звучит грандиозно. Особенно забавно, если там реально кодогенерация с С++11 будет действительно на уровне рукопашного ассемблера.
Но пока переходить не буду, шас цель в оптимизации не стоит, пока я осваиваю архитектуру. А LLVM-MOS про уход на более высокий уровень абстракции. Если начну делать свой большой проект, то мб и перейду.

Sharpnull,  разработчики LLVM-MOS как раз утверждают, что код генерируется с точностью до такта и приводят демку, где необходим точный расчет тактов. Но я не вникал пока, может и врут. Но даже если там кодогененератор будет работать на уровне сс65, то использование  оправдано, ибо современный С++ сильно упрощает разработку. Хотя фишками С++11 и старше я особо так и не начал пользоваться.

30
Sharpnull,  спасибо, все заработало.
Сколько тут все-таки тонкостей. Страшно представить разработку большой игры для NES, разработчикам прошлого большой респект.

Страницы: [1] 2 Далее