Векторы прерывания в формате программирование на ассемблере 6502, можно представить как всем хорошо известный патерн event-observer в высокоуровневых языках программирования. Конечно же можно реализовать данный патерн и на ассемблере но я его привел для большего понимания работы вектора прерывания.
Если помните раннее я уже упоминал про память в 6 байт (3 слова, по 16 бит), которые выделены для секции VECTOR данный сегмент содержит всего 3 адреса процедур
.segment "VECTORS" .addr nmi_isr, reset, irq_isr
Когда ассемблер соберет код в бинарный файл заместо меток будет установлен соответствующий адрес данных процедур, которые обработают определенное прерывания. Всего их 3 в Famicom
-
NMI — Not Masked Interrupt — событие срабатывающие при генерации каждого кадра то есть 60 раз в секунду. Тут будет отрабатывать код для отрисовки кадра игры.
-
RESET — Событие перезагрузки — тут мы будем инициализировать программу, исходя из названия данное прерывание срабатывает при перезагрузки/загрузки процессора
-
IRQ — Interrupt request — прерывание которое может быть запущенно по запросу какого либо оборудования
Я в своей игре пока не использую IRQ по этому процедура выглядит довольно просто
.proc irq_isr RTI .endproc
RTI — return from interrupt возврат в прерывание, это довольно важное замечание забегая вперед скажу что в остальных случаях объявление .proc будет выполнять инструкцию RTS — Return from subroutine что буквально вернуть в подпрограмму.
Мы плавно подошли к понятию .proc (procedure), сам по себе .proc определяет некую область видимость где могут быть использованы и определенны одноименные метки, но в контексте процедуры возможен только переход на метки данной процедуры. К примеру:
.proc foo LDA var CMP #$01 BEQ return LDA var2 STA var3 return: RTS .endproc .proc bar LDA var CMP #$02 BEQ return LDA var2 STA var3 return: RTS .endproc .proc main JSR foo JSR bar .endproc
Но не только саброуты (процедуры) они еще позволяют структурировать наш код в более понятные секции как функции/процедуры/методы в других языках. И еще один огромный плюс, конструкции ветвления JMP, BEQ, BNE, BCC, BCS могут перейти только на интервал -128-+128 строк, то есть в пределах адресации лимитированные 1 байтом (максимальное число 256) а вот процедуры по факту не имеют такого ограничения.
Выше в подпрограмме я привел пример как вызвать процедуру, с помощью инструкции JSR — jump to SubRoutine с помощью нее мы переходим в подпрограмму, выполняем какие то действия и в окончание в подпрограмме которую мы вызвали происходит вызов инструкции RTS, которая возвращает нас обратно в процедуру которая до этого вызвала эту подпрограмму, и родительская подпрограмма продолжает выполняться дальше.
Ассемблер ca65 и многие другие ассемблеры позволяют выполнять инструкции .include «filename.asm» которая включает файлы в главный файл игры. Интересно что в каждом файле мы можем определить нужные нам секции, которые определены в ini файлы конфигурации линкера который определяет в какой диапазон памяти положить ту или иную секцию кода, но это уже разговор для следующих статей.
Полезные ссылки:
-
https://youtu.be/KGXwoqYJ9Dk — видео/аудио формат статьи
-
https://github.com/lnroma/newGameNes — репозиторий с кодом игры
-
https://habr.com/ru/post/715994/ — считывание контроллера
-
https://habr.com/ru/post/551488/ — вводная статья
-
https://habr.com/ru/post/715994/ — прокрутка фона
-
https://habr.com/ru/post/553848/ — создание графики nes/dendy
ссылка на оригинал статьи https://habr.com/ru/post/716254/
Добавить комментарий