# MSBuild Properties
.NES exposes several MSBuild properties that you can set in your `.csproj` file to
configure how your NES ROM is built. All properties are optional and have sensible
defaults for simple projects.
## Properties
### `NESDiagnosticLogging`
Enable verbose diagnostic output during transpilation.
| | |
|---|---|
| **Type** | `bool` |
| **Default** | `false` |
When set to `true`, the transpiler logs detailed information about the IL it
reads and the 6502 instructions it emits. Useful for debugging transpilation
issues.
```xml
true
```
### `NESMirroring`
Controls the nametable mirroring mode stored in the iNES header (Flags6, bit 0).
| | |
|---|---|
| **Type** | `string` |
| **Default** | `Horizontal` |
- `Horizontal` — horizontal mirroring, used for vertical scrolling games (the default).
- `Vertical` — vertical mirroring, used for horizontal scrolling games.
```xml
Vertical
```
See the `samples/statusbar` and `samples/horizscroll` projects for examples.
### `NESMapper`
Specifies the iNES mapper number for the cartridge hardware.
| | |
|---|---|
| **Type** | `int` |
| **Default** | `0` (NROM) |
Common mapper values:
| Mapper | Name | Description |
|--------|------|-------------|
| 0 | NROM | No bank switching (32 KB PRG, 8 KB CHR) |
| 1 | MMC1 | 16 KB switchable PRG banks, 4 KB switchable CHR banks, software mirroring control |
| 2 | UxROM | 16 KB switchable PRG banks, fixed CHR |
| 3 | CNROM | Fixed PRG, switchable 8 KB CHR banks |
| 4 | MMC3 | Switchable PRG and CHR banks with IRQ counter |
The mapper number is encoded in bits 4–7 of iNES Flags6 and Flags7.
```xml
4
```
See the `samples/bankswitch` project for an MMC3 example.
### `NESPrgBanks`
Number of 16 KB PRG ROM banks.
| | |
|---|---|
| **Type** | `int` |
| **Default** | `2` (32 KB) |
This value is written to byte 4 of the iNES header. Increase it for larger games
that use bank switching.
```xml
4
```
### `NESChrBanks`
Number of 8 KB CHR ROM banks.
| | |
|---|---|
| **Type** | `int` |
| **Default** | `1` (8 KB) |
This value is written to byte 5 of the iNES header. Set to `0` if your game uses
CHR RAM (pattern data uploaded at runtime instead of stored on the cartridge).
```xml
8
```
### `NESMmc3BankedLayout`
Enable deterministic mapper-4 physical bank placement. This mode requires
`NESMapper=4` and at least two 16 KiB `NESPrgBanks`.
| | |
|---|---|
| **Type** | `bool` |
| **Default** | `false` |
When enabled, dotnes links the transpiled C# program at `$C000` across the
final two physical 8 KiB PRG banks. The second-last bank is mapped at `$C000`
only in PRG mode 0; the last bank is always fixed at `$E000`. A reset stub in
the last bank selects PRG mode 0 before jumping to `$C000`, and the NMI/RESET/IRQ
vectors are written at `$FFFA-$FFFF`. Runtime code must keep MMC3 bank-select
bit 6 clear so it does not unmap the program at `$C000`. Other PRG assets can
be assigned to the switchable `$8000` or `$A000` windows with `NESPrgBank`
items. Banked layout supports at most 32 `NESPrgBanks` and 32 `NESChrBanks`.
```xml
4
4
2
true
```
See `samples/bankswitch` and [MMC3 bank layout](mmc3-bank-layout.md).
### `NESBattery`
Indicates that the cartridge has battery-backed SRAM at $6000-$7FFF.
| | |
|---|---|
| **Type** | `bool` |
| **Default** | `false` |
When set to `true`, bit 1 of iNES Flags6 is set, telling emulators to persist
the 8 KB SRAM region across power cycles. Use `peek()` and `poke()` with
addresses in the $6000-$7FFF range (constants `SRAM_START` and `SRAM_END`) to
read and write save data.
```xml
true
```
### `MesenTestRunner`
Run Mesen in headless test-runner mode (`--testrunner --doNotSaveSettings`).
Useful for CI smoke tests.
| | |
|---|---|
| **Type** | `bool` |
| **Default** | `false` |
```xml
true
```
### `MesenTimeout`
Auto-exit Mesen after this many seconds. Only meaningful in test-runner mode.
| | |
|---|---|
| **Type** | `int` (seconds) |
| **Default** | *(empty — uses Mesen default of 100)* |
```xml
10
```
### `MesenLuaScript`
Path to a Lua script to load when running Mesen.
| | |
|---|---|
| **Type** | `string` (file path) |
| **Default** | *(empty)* |
```xml
scripts/smoke-test.lua
```
Example using `dotnet run` on the command line:
```bash
dotnet run -p:MesenTestRunner=true -p:MesenTimeout=10 -p:MesenLuaScript=smoke-test.lua
```
## Item Groups
### `NESPrgBank`
Place a `.bin` or ca65-compatible `.s` asset in one physical 8 KiB MMC3 PRG
bank. `Bank` is the zero-based physical bank number, `CpuAddress` is the CPU
window used to link the asset (`0x8000` or `0xA000`), and `Offset` is an
optional byte offset within the bank.
```xml
```
The final two physical PRG banks are reserved for the fixed transpiled program.
Assembly assets support the existing label relocations for absolute
instructions, low/high-byte immediates, and `.word`/`.addr` data. A bank must
be selected at runtime before code accesses it; dotnes does not insert mapper
writes automatically.
### `NESChrBank`
Place a `.bin` or `.s` asset in one physical 1 KiB MMC3 CHR bank. `Bank` is
the zero-based physical bank number and `Offset` is an optional byte offset
within that bank. Assembly assets must contain a `CHARS` segment.
```xml
```
Legacy `CHARS` segments from `NESAssembly` continue to populate the start of
CHR ROM. Explicit `NESChrBank` items cannot overlap those bytes.
### `NESAssembly`
Include pattern for 6502 assembly (`.s`) files that provide CHR ROM data or
external subroutines.
| | |
|---|---|
| **Default** | `*.s` (all `.s` files in the project directory) |
By default, every `.s` file in your project directory is included. The most
common use is a `chr_*.s` file that contains your game's tile/sprite graphics.
```xml
```
## Full Example
A project using bank switching with MMC3, vertical mirroring, and diagnostic
logging:
```xml
Exe
net10.0
enable
enable
4
4
8
Vertical
true
true
```
## Output
The transpiler produces a `.nes` ROM file at:
```
$(OutputPath)$(TargetName).nes
```
For example, building a project called `hello` in Debug configuration produces
`bin/Debug/net10.0/hello.nes`.
## Incremental Builds
The `Transpile` target uses MSBuild incremental build support (`Inputs`/`Outputs`)
to avoid re-transpiling when nothing has changed. The inputs include:
- `$(TargetPath)` — the compiled `.dll`
- `@(NESAssembly)` — the `.s` assembly files
- A **properties stamp file** — tracks changes to `NESMirroring`, `NESMapper`,
`NESPrgBanks`, `NESChrBanks`, `NESBattery`, `NESMmc3BankedLayout`, and bank
item metadata
A `_WriteNESPropertiesStamp` target automatically runs before each transpilation
and writes the current property values to
`$(IntermediateOutputPath)dotnes.properties.stamp`. The file is only rewritten
when a value actually changes (`WriteOnlyWhenDifferent`), so toggling a property
like `NESBattery` from `false` to `true` will correctly retrigger transpilation
on the next build without causing unnecessary rebuilds.
### `TranspileDependsOn`
A semicolon-separated list of targets that the `Transpile` target depends on.
By default this includes `_WriteNESPropertiesStamp` (the stamp file target
described above). You can append your own targets to run custom logic before
transpilation:
```xml
$(TranspileDependsOn);MyCustomPreTranspileTarget
```