Решил углубиться в тему разработки игр для NES / Famicom / Dendy. Выбор пал на Assembler, хотя видел проекты и на Си, но Assembler ближе к железу, что позволит лучше понять суть работы приставки.
Для начала приведу список некоторых интернет-ресурсов, которые могут оказаться полезны тем, кто захочет освоить написание игр под NES:
- Nesdev Wiki - много полезной информации (англ).
- MOS Technology 6502/Система команд - описание команд процессора 6502 (рус). Учтите, что в NES используется урезанная версия процессора 6502, в которой вырезан блок двоично-десятичной арифметики BCD, процессор не умеет работать в этом режиме.
- cc65 - C compiler for 6502 - Компилятор Си под 6502 (англ). В составе имеется Ассемблер (ca65), именно его и использую для работы.
- Программирование процессора 6502 (Programming the 6502). Программирование Денди - про Ассемблер 6502 (рус). Про Денди по факту там ничего нет.
- Famicom Party. Making NES Games in Assembly - книга о программировании NES (англ). К сожалению, не дописана.
- Создание игр для NES на ассемблере 6502 - перевод указанной выше книги (рус). К сожалению, переведена не до конца, то есть на русском еще меньше глав, чем в самой книге, которая не дописана.
- Notepad++ - редактор для редактирования кода с подсветкой. Тема для подсветки синтаксиса Ассемблера 6502 прикреплена к сообщению (тему подсветки разработал самостоятельно, привязывается к файлам с расширениями asm, inc и mac). Для установки темы делаем следующее: Синтаксис - Пользовательский синтаксис - Задать свой синтаксис - Импорт, выбираем файл Asm6502.xml из прикрепленного архива.
Редактор удобен тем, что позволяет настроить компиляцию и запуск программы нажатием одной кнопки. Для компиляции исходного кода написал небольшой CMD-файл:
@echo off
title Compilation NES
%~d1
cd "%~p1"
if exist "%~n1.nes" del /q "%~n1.nes"
if exist "%~n1.o" del /q "%~n1.o"
ca65 "%~1"
if errorlevel 1 goto error
ld65 "%~n1.o" -t nes -o "%~n1.nes"
if errorlevel 1 goto error
del /q "%~n1.o"
echo OK
"%~n1.nes"
goto :end
:error
pause
:end
Код сохранил в файл compilation_nes.cmd и поместил в папку с Ассемблером (у меня это C:\cc65\bin). Для настройки Notepad++ на данный компилятор необходимо выполнить следующие действия:
- Папку с Ассемблером C:\cc65\bin добавить в список путей PATH операционной системы (речь за Windows).
- В Notepad++ нажать F5, ввести команду: compilation_nes.cmd "$(FULL_CURRENT_PATH)"
- Далее "Сохранить...", набрать название команды, например, "Compilation NES" и выбрать клавишу быстрого доступа. Я выставил запуск на клавишу F9.
Теперь при нажатии F9 программа компилируется и запускается получившийся .nes-файл (расширение файла должно быть привязано к одному из эмуляторов NES).
У этого способа есть одно ограничение: запускать программу надо только тогда, когда в редакторе открыта вкладка с главным файлом проекта, с которого подключены все остальные файлы. Например, у меня в одном из тестовых проектов это выглядит так:
Никаких .export и .import быть не должно, все файлы проекта надо подключать с помощью .include. Но это вообще не проблема, а даже наоборот, не надо думать о том, какие объекты экспортировать и импортировать, они все доступны из любого файла проекта. Меньше писанины и кучи лишних .include в каждом файле проекта (то есть все .include делаются один раз в главном файле).
В общем, инструмент достаточно удобный, можете пользоваться, но не навязываю.
--------------------------------------------------------------------------------------------------
Добавлено позже:Ну и тема создана для вопросов и для всевозможных заметок и аккумуляции полезной информации. И первый вопрос будет от меня. А вопрос по указанной в ссылках книге, а именно по главе
10. Sprite GraphicsВ книге приводят код обработчика прерывания NMI:
.proc nmi_handler
LDA #$00
STA OAMADDR
LDA #$02
STA OAMDMA
RTI
.endproc
В последующих главах обработчик прерывания NMI усложняется, в него добавляются новые инструкции. Вопрос возник на счет сохранения контекста, то есть регистров.
Смотрим описание команды
RTI:
Как видно, регистр флагов P восстанавливается из стека, после чего происходит возврат из прерывания. Однако никакие другие регистры не восстанавливаются (а в примере выше мы меняем значение аккумулятора A).
Вопрос в том, где ошибка: либо в описании команды RTI, и она в реальности восстанавливает значения всех регистров, либо в книге, и заниматься сохранением регистров должен программист самостоятельно. То есть переписать обработчик таким образом:
.proc nmi_handler
PHA ; сохраняем в стеке аккумулятор A
LDA #$00
STA OAMADDR
LDA #$02
STA OAMDMA
PLA ; восстанавливаем аккумулятор перед возвратом
RTI
.endproc
Так как с регистрами X и Y мы тут не работаем, то их сохранять необязательно.
Кто что думает по этому поводу?