Fire
Cross mapper cc65/ca65 NES template.
Install / Use
/learn @AlchemicRaker/FireREADME
Fire NES Template
A thin NES template that builds against multiple mappers and includes the most fundamental of mapper integrations (eg bank switching and far calling across banks). This project enables you to develop in both C and assembly.
Project Goal
This project's goal is to provide an NES template useful to both novice and advanced developers. This affects many aspects of this template:
- Low opinion design so that your game is not limited by unnecessary and incorrect constraints about how the template thinks your game should run.
- Module system with ready-to-use Easy Joypad input library, Famistudio and BHOP (wip) audio engines, and the Rapidfire video library.
- Multi-mapper build system that targets many common mappers used in the homebrew community. Start developing with any of these mappers today.
- Unified mapper API. PRG banking, CHR banking, seamless far calling, IRQ-based screen scrolling; all in a unified api.
- C and ASM are both supported, though you may choose to disable all C elements of the template if you prefer the bare-metal experience.
- Highly commented system files for advanced developers to modify as they desire.
- Ready-to-use segments reserved for your game's startup, NMI, and IRQ assembly code.
This project strongly recommends using VSCode, paired with Mesen-X and the Alchemy65 VSCode extension for the best NES debugging experience. This project contains a launch configuration for VSCode, and a build task that will display build errors in your source code.
Project Organization
The Fire template organizes code into four major sections, that correspond with top-level folders in the project.
- "sys/" contains the template's core code and mapper definitions for the unified API.
- "lib/" is for modules that you can optionally include in your project. Choose which modules to include by setting MODULES in the makefile.
- "src/" is for all of your game code, entered via
main()ormain:. Assembly files in this folder will be included automatically. If you have enabled C_SUPPORT, C files in this folder will be included automatically. - "res/" is for your game resources. Assembly files in this folder will be included automatically.
Getting Started
Before you get started, install make for Windows (or sudo apt-get install build-essential for ubuntu) and cc65 and add them to your environment PATH.
git clone git@github.com:AlchemicRaker/fire.git
cd fire
make
Now you should have a fire.nes rom you can open in an emulator of your choice.
If you have set up make, Mesen-X (add them to your PATH!), VSCode, and installed the Alchemy65 extension, you may use
ctrl+shift+bto build the project, andF5to run it with debugging.
Unified Mapper API
PRG Configuration Overview
| Mapper | PRG BANK | DATA BANK | SAMPLE BANK | PRG FIXED | IRQ | Notes | | ------ | -------- | --------- | ----------- | --------- | --- | ----- | | NROM | - | - | - | $8000 | No | | | UxROM | $8000 | - | - | $C000 | No | | | MMC1 | $8000 | - | - | $C000 | No | | | MMC3 | $8000 | $A000 | - | $C000 | Yes | PRG mode 0 | | MMC5 | $8000 | $A000 | $C000 | $E000 | Yes | PRG mode 3 | | FME-7 | $8000 | $A000 | $C000 | $E000 | Yes | | | VRC6 | $8000 | $C000 | - | $E000 | Yes | (VRC6a) | | VRC7 | $8000 | $A000 | $C000 | $E000 | Yes | | | N163 | $8000 | $A000 | $C000 | $E000 | Yes | | | ~~GTROM~~ | $8000 | $A000 | $C000 | $E000 | No | plans below |
Many of these mappers have configurable layouts. This template assumes that banked prg rom will use the lower addresses, and static prg rom will use the higher addresses. When possible, modes with the highest number of prg banks have been used (see "notes" column).
GTROM and compatible / comparable mappers have 32K PRG banks. With GTROM's max capacity of 512K, that means there are 16 banks of memory that can be swapped in. The plan is to "fake" PRG, DATA, and SAMPLE BANKs, so long as their combined number of combinations is 16 or less. For instance, 1 x PRG, 4x DATA, and 4x SAMPLE BANKs would be a valid configuration.
PRG API Reference
The availability of PRG features corresponds with the overview table above. Your game's code is entered at main() or main:, which must be located in "PRG_FIXED" (or another non-banked segment).
The PRG BANK is specially designed to be easy for a developer to navigate with code. In C you can use the "farcall" wrapper that will automatically handle bank switching when calling wrapped functions.
// wrap one or more functions with the "farcall" wrapper in your headers.
// these functions _must_ be void and take zero arguments.
#pragma wrapped-call (push, farcall, bank)
void pause_menu(void);
#pragma wrapped-call (pop)
// bank switching now happens automatically when calling those functions.
if(PAUSE) {
pause_menu();
}
For assembly, the "farjsr" and "farjmp" macros are provided (via "fire.inc") that take a label as a target, and will switch to the label's bank before jumping to the address.
.include "fire.inc" ; to get access to the macros
.segment "PRG_BANK_0"
loop:
farjsr work
jmp loop
back:
farjmp forth
.segment "PRG_BANK_1"
work:
lda #$42
rts
forth:
farjmp back
The following functions are available for DATA BANK and SAMPLE BANK. The DATA BANK has optional C functions to push and pop banks, that may help manage which data page is currently selected.
void push_data_bank(char bank); //#ifdef DATA_SUPPORT
void pop_data_bank();
void select_data_bank(char bank);
void select_sample_bank(char bank); //#ifdef SAMPLE_SUPPORT
Bank selection is available with assembly subroutines of the same names:
lda #bank
jsr select_sample_bank
lda #bank
jsr select_data_bank
CHR Configuration Overview
| Mapper | 8K Select | 4K Select | 2K Select | 1K Select | Mirroring | CHR Windows | | ------ | --------- | --------- | --------- | --------- | --------- | ----------- | | NROM | - | - | - | - | Fixed | None | | UxROM | - | - | - | - | Fixed | None | | MMC1 | Yes | Yes | - | - | Dynamic | 4K+4K or 8K | | MMC3 | Yes | Yes | Yes | Sprite or Background | Dynamic | 2Kx2 + 1Kx4 | | MMC5 | Yes | Yes | Yes | Yes | Dynamic | 1Kx8 (and more) | | FME-7 | Yes | Yes | Yes | Yes | Dynamic | 1Kx8 | | VRC6 | Yes | Yes | Yes | Yes | Dynamic | 1Kx8 | | VRC7 | Yes | Yes | Yes | Yes | Dynamic | 1Kx8 | | N163 | Yes | Yes | Yes | Yes | Dynamic | 1Kx8 + 1Kx4(NT) | | ~~GTROM~~ | Yes | - | - | - | N/A | 8K |
MMC3 includes two modes, the more commonly used "1K sprites", and the less commonly used "1K backgrounds". You must include
MMC3_1K_SPRITES(forselect_chr_1k_1xxx) orMMC3_1K_BACKGROUNDS(forselect_chr_1k_0xxx) in the makefile OPTIONS in order to build MMC3.
CHR API Reference
These functions are available based on the overview table above.
Banks are always 0-indexed, and counted in multiples according to the function's name.
As an example, select_chr_2k_1800(2) selects $1000-$17FF from CHR ROM to map into $1800-$1FFF of PPU memory. This is equivalent to calling select_chr_1k_1800(4); and select_chr_1k_1C00(5);.
void select_mirror_vertical(); //#ifdef DYANIC_MIRRORING
void select_mirror_horizontal();
void select_chr_8k_0000(char bank) //#ifdef CHR_8K_SUPPORT
void select_chr_4k_0000(char bank) //#ifdef CHR_4K_SUPPORT
void select_chr_4k_1000(char bank)
void select_chr_2k_0000(char bank) //#ifdef CHR_2K_SUPPORT
void select_chr_2k_0800(char bank)
void select_chr_2k_1000(char bank)
void select_chr_2k_1800(char bank)
void select_chr_1k_0000(char bank) //#ifdef CHR_1K_B_SUPPORT
void select_chr_1k_0400(char bank)
void select_chr_1k_0800(char bank)
void select_chr_1k_0C00(char bank)
void select_chr_1k_1000(char bank) //#ifdef CHR_1K_S_SUPPORT
void select_chr_1k_1400(char bank)
void select_chr_1k_1800(char bank)
void select_chr_1k_1C00(char bank)
All of these C functions are available as assembly subroutines with the same names, and use the accumulator register as the bank argument. The above example may be written in assembly as:
lda #$02
jsr select_chr_2k_1800
Vector Design
Startup, NMI, and IRQ vectors contain very common bits of code, but also contain code that is highly game-specific. We understand how critical timing is in these places, so we have provided that common code (as described below) and left the rest open for you.
Startup Vector Implementation
STARTUP_FIRE_1- Boilerplate initialization of NES state, including zeroing out the ZP
- If C is enabled, sp and the necessary BSS and DATA ranges will be initialized
- Waits through t
