nes-pascal

Target platform

English Português (Brasil)

NES Pascal generates ca65-compatible Assembly for the Ricoh 2A03 CPU and uses only 6502 instructions. It does not generate intermediate C or use 65C02-only instructions.

ROM format

Generated programs target NTSC NES systems with mapper 0, NROM-256:

The iNES header defaults to horizontal nametable mirroring. Pass --mirroring vertical for vertical mirroring; only these two static NROM choices are supported.

The output image is 40,976 bytes: a 16-byte iNES header, 32 KiB of PRG-ROM, and 8 KiB of CHR-ROM.

Startup behavior

The RESET path disables interrupts, initializes the stack and runtime-owned RAM, and waits for the PPU to stabilize. Initialization palette writes remain safe while rendering is disabled. A configured nametable is copied to $2000-$23FF during this disabled-rendering phase. nes.run waits for VBlank before enabling NMI and rendering.

Each NMI preserves A, X, Y, and stack balance, increments one volatile 8-bit frame counter, and sets an advisory frame-ready byte. If registered, one restricted VBlank callback runs through direct JSR; its procedure ends with RTS. NMI then restores the compiler-owned PPU state, restores registers, and owns the final RTI. Ordinary update logic runs only on the main thread. Its persistent last-processed frame byte preserves a pending NMI across slow callbacks and coalesces older backlog. The main thread polls standard controller ports once before each processed update. When sprite support is linked, NMI uploads the complete page-aligned OAM shadow by DMA; the legacy fixed sprite-0 helper first commits its complete staging record. Metasprite geometry is composed into that shadow in main/update context; NMI does no layout calculation. There is no generic PPU command queue, automatic animation system, controller remapping, or audio.

Memory

The NES exposes 2 KiB of physical internal CPU RAM at $0000-$07FF. $0800-$1FFF contains mirrors, not additional storage. Zero Page has separate runtime, compiler-temporary, future-explicit, and automatic-promotion regions. Frequently referenced globals may be promoted conservatively; parameters and fallback variables use regular RAM. See CPU memory for the complete deterministic policy.

CHR-ROM is normally empty. One exactly 8192-byte raw file can be selected with the compiler’s --chr option; relative paths use the source file directory. The controller example conditionally embeds two internal 8x8 player tiles when no file is selected. Configured CHR bytes occupy the existing CHR segment once and are not converted or modified. Configured metasprite JSON is parsed only by the compiler. Its compact component geometry is emitted in PRG-ROM, while referenced tile graphics stay in the one configured CHR bank.

One complete 1024-byte nametable can be embedded in PRG-ROM and uploaded to PPU nametable 0. It contains 960 tile indexes followed by the 64-byte attribute table. The compiler also accepts those two portions as separate files and concatenates them without conversion. See Background loading.

The generated Assembly is intentionally readable and includes comments that identify the source of generated blocks. The backend is not a generic game engine.