Считывание контроллера nes (dendy) на ассемблере

от автора

В предыдущей статье Я рассказывал вам бо основах ассемблера 6502 о некоторых участках памяти и как с ними работать, до сегодняшнего дня у меня стояли следующий ряд задач: считывание контроллера, анимация, таймер. Ниже под катом Я расскажу как научился читать контроллер.


Всё как и ранее началось с цели, я пошел на wiki nesdev (ссылка будет в конце статьи) дабы прочитать о том как работать с контроллером. Ведь если разобраться, для игры существует всего несколько внешних триггеров, по срабатыванию которых игра выполняет какие то определенные действия. Во-первых, это внутренний таймер, действия персонажей выполняются по наступление какого либо времени, ярким примером такого могут служить простые боссы в играх, их поведение описано шаблоном, который привязан к какому либо временному отрезку. Во-вторых, важным триггером для игры является нажатие кнопок контроллера, на которые в основном отвечает главный герой игры, при нажатие вверх, вниз, влево и вправо, start и select. Герой может двигаться, прыгать, ползти и так далее. То есть нажатие кнопки говорит непосредственно игре сделай то то. Забегая вперед программируя под NES я пытаюсь ориентироваться не на объект который будет на экране, как возможно многие современные программисты, а что будет нарисовано в том или ином кадре.

Но ближе к теме, контроллеров на NES очень много начиная от классических и заканчивая надувными мотоциклами и микрофонами, но у денди было всего два классический геймпад (жойстик) и световой пистолет. О последним возможно поговорим позже. И так в денди есть два порта процессора для чтения контроллеров $4016 для 1-го геймпада, $4017 для второго геймпада.

Первым делом необходимо «перезагрузить контроллер» или «инициализировать» его, записав просто в порт последовательно значения $01 и $00

deadyJoy:  lda #$01  sta $4016  lda #$00  sta $4016

Далее денди считывает нажатие в следующей последовательности A, B, Select, Start, Up, Down, Left, Right и записывает в порт $4016, так что что бы получить нажатие кнопки, нам лишь потребуется прочитать из этого порта, значение последовательно 8 раз

lda #4016 ; A lda #4016 ; B lda #4016 ; Select lda #4016 ; Start lda #4016 ; Up lda #4016 ; Down lda #4016 ; Left lda #4016 ; Right

В принципе мы уже в аккумулятор A загружаем признак нажатия кнопки. Но этот код явно надо допилить что бы при нажатие кнопки, происходило некое движение спрайта героя. Это я решил тем что написал небольшой обработчик для нажатия каждой кнопки контроллера. И если она отпущена то герой стоит на месте если нажата то происходит увеличение переменной с координатой X и Y героя. Опять же забегая в перёд переменные хранятся в разделе zeropage (нулевая страница) раздел с $00 — $FF Я так понимаю специально сделан для хранения неких переменных, в силу ограничений памяти, но могу ошибаться, буду рад если кто то поправит меня по этому поводу в комментариях.

ReadA:   LDA $4016       ; Кнопка A   AND #%00000001 ; логичиское меняет флаг N и Z   bne walkHeroRight ; если нажата кнопка A   beq ReadB ; если не нажата переходим к чтению B  ReadB:   LDA $4016       ; Кнопка B   AND #%00000001    bne walkHeroLeft ; если нажата B walkHeroLeft для тестирования того что срабатывает   beq ReadSelect ; Переходим к чтению Select     ; тут 5 раз такого же кода для обработки героя  ReadRight:   LDA $4016       ; Крестовина влево   AND #%00000001   bne walkHeroRight ; если креставина нажата в право   beq heroStay ; если не нажата ниодна кнопка герой стоит на месте

Я пока только изучаю assembler по этому избегаю сокращения конструкций так более понятно и для меня, и для статьи, для тех людей которые бы хотели освоить его так же как и Я. Данный код при рисование каждого кадра, последовательно проверяет нажатие каждой кнопки и в случае того если не было ни одного нажатия герой стоит. В случае если было нажатие какой либо кнопки программа переходит в соответствующий раздел кода. Где увеличивается или уменьшается переменная X и Y координаты

walkHeroRight:   inc herroXCoordinate   jmp drawHero  walkHeroLeft:   dec herroXCoordinate   jmp drawHero

Далее идет рисование героя

 drawHero:   lda herroYCoordinate   sta $2004   lda $00   sta $2004   lda #%00010111   sta $2004   lda herroXCoordinate   sta $2004    lda herroYCoordinate   sta $2004   lda #01   sta $2004   lda #%00010111   sta $2004   lda herroXCoordinate   adc #$07   sta $2004    lda herroYCoordinate   adc #$07   sta $2004   lda $02   sta $2004   lda #%00010111   sta $2004   lda herroXCoordinate   sta $2004    lda herroYCoordinate   adc #$07   sta $2004   lda #03   sta $2004   lda #%00010111   sta $2004   lda herroXCoordinate   adc #$07   sta $2004    nmi_delay 4   jmp mainLoop

И цикл повторяется: считывание, определение, рисование состояния героя.
Вот так оказалось не все так сложно, как казалось на первый взгляд. Больше сложностей вызывает сам ассемблер, иногда некоторые моменты не так прозрачны, но Я хотел бы сказать одну простую вещь, если есть мечта стоит идти к ней, и не в коем случае не стоит бросать таких начинаний. В следующих статьях Я бы хотел поговорить о анимации и создание графики.

Прошлая статья
Литература: http://wiki.nesdev.com/
Пример полностью на github

ссылка на оригинал статьи https://habr.com/ru/post/552604/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *