docs/language-guide.md (895 lines): Complete reference for every language feature with code examples. Covers: program structure, types, expressions, statements, assets, mappers, error codes, and compiler flags. docs/architecture.md (130 lines): Compiler pipeline overview, module descriptions, testing guide. docs/nes-reference.md (190 lines): NES hardware quick reference: CPU, memory map, PPU, iNES format. https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
8.6 KiB
NES Hardware Quick Reference
A concise reference to the NES hardware for NEScript contributors. Understanding these constraints explains many of the compiler's design decisions.
CPU: Ricoh 2A03 (MOS 6502 variant)
- Clock speed: 1.79 MHz (NTSC), 1.66 MHz (PAL)
- Architecture: 8-bit data bus, 16-bit address bus
- Registers:
A(Accumulator) -- 8-bit, used for arithmetic and logicX(Index X) -- 8-bit, used for indexing and countingY(Index Y) -- 8-bit, used for indexing and countingSP(Stack Pointer) -- 8-bit, points into the $0100-$01FF rangeP(Status) -- 8-bit flags: N(egative), V(overflow), B(reak), D(ecimal), I(nterrupt), Z(ero), C(arry)PC(Program Counter) -- 16-bit
- No multiply, divide, or floating point instructions
- Instruction set: 56 official opcodes with multiple addressing modes (implied, immediate, zero page, zero page X/Y, absolute, absolute X/Y, indirect, indexed indirect, indirect indexed)
Why This Matters for NEScript
The 6502's 3-register architecture drives the compiler's register allocator. The lack of multiply/divide means the compiler emits software routines for *, /, and %. Zero-page addressing is 1 byte shorter and 1 cycle faster than absolute addressing, which is why fast variable placement matters.
Memory Map
| Address Range | Size | Description |
|---|---|---|
$0000-$00FF |
256 bytes | Zero page -- fast access |
$0100-$01FF |
256 bytes | Stack (grows downward) |
$0200-$02FF |
256 bytes | OAM shadow buffer (convention) |
$0300-$07FF |
1280 bytes | General purpose RAM |
$0800-$1FFF |
-- | Mirrors of $0000-$07FF |
$2000-$2007 |
8 bytes | PPU registers |
$2008-$3FFF |
-- | Mirrors of PPU registers |
$4000-$4017 |
24 bytes | APU and I/O registers |
$4018-$401F |
8 bytes | CPU test mode (normally disabled) |
$4020-$5FFF |
-- | Expansion ROM (mapper-dependent) |
$6000-$7FFF |
8 KB | SRAM / PRG RAM (if present) |
$8000-$BFFF |
16 KB | PRG ROM lower bank |
$C000-$FFFF |
16 KB | PRG ROM upper bank (or fixed bank) |
Interrupt Vectors
| Address | Vector | Description |
|---|---|---|
$FFFA |
NMI | Non-Maskable Interrupt (vertical blank) |
$FFFC |
RESET | Power-on / reset entry point |
$FFFE |
IRQ | Interrupt Request (mapper-dependent) |
NEScript Memory Usage
The compiler reserves the following:
$00-$0F: System use (frame counter, temp registers, NMI flags)$10-$FF: Available forfastvariables and compiler-promoted variables$0200-$02FF: OAM shadow buffer (DMA'd to PPU each frame)$0300-$07FF: General variables, state-local storage
PPU (Picture Processing Unit)
- Resolution: 256 x 240 pixels
- Refresh rate: 60 Hz (NTSC), 50 Hz (PAL)
- VRAM: 2 KB internal (2 nametables), expandable by mapper
- Pattern tables: 2 tables of 256 tiles each (one for backgrounds, one for sprites), stored in CHR ROM/RAM
- Tile size: 8x8 pixels (or 8x16 for tall sprites)
- Color depth: 2 bits per pixel (4 colors per tile, selected from a sub-palette)
Nametables
- 4 logical nametables, each 960 bytes of tile indices + 64 bytes of attribute data
- With 2 KB VRAM, 2 physical nametables exist; the other 2 are mirrors
- Mirroring arrangement (horizontal or vertical) is set by the cartridge/mapper
Sprites (OAM)
- 64 sprites total, each defined by 4 bytes:
- Byte 0: Y position
- Byte 1: Tile index
- Byte 2: Attributes (palette, priority, flip H/V)
- Byte 3: X position
- 8 sprites per scanline maximum (excess sprites are dropped)
- OAM is 256 bytes, typically shadow-buffered at CPU
$0200and DMA'd via$4014
Palettes
- Background: 4 sub-palettes, each with 4 colors (first color shared across all)
- Sprite: 4 sub-palettes, each with 4 colors (first color is transparent)
- Colors are indices into the NES master palette (64 entries,
$00-$3F) - Palette RAM is 32 bytes total at PPU
$3F00-$3F1F
PPU Registers
| Address | Name | Description |
|---|---|---|
$2000 |
PPUCTRL | NMI enable, sprite size, pattern table select, nametable select |
$2001 |
PPUMASK | Color emphasis, sprite/background enable, clipping |
$2002 |
PPUSTATUS | Vblank flag, sprite 0 hit, sprite overflow |
$2003 |
OAMADDR | OAM address for writes |
$2004 |
OAMDATA | OAM data read/write |
$2005 |
PPUSCROLL | Scroll position (write twice: X, Y) |
$2006 |
PPUADDR | VRAM address (write twice: high, low) |
$2007 |
PPUDATA | VRAM data read/write |
Rendering Timing
- Vblank starts at scanline 241 and lasts ~20 scanlines (~2,273 CPU cycles)
- PPU updates (palette, nametable, scroll) must happen during vblank
on framecode runs during the visible frame; the implicitwait_frame()yields until the next vblank- Approximately 29,780 CPU cycles per frame (NTSC)
APU (Audio Processing Unit)
| Channel | Type | Description |
|---|---|---|
| Pulse 1 | Square wave | Variable duty cycle (12.5/25/50/75%) |
| Pulse 2 | Square wave | Same as Pulse 1 |
| Triangle | Triangle | Fixed volume, good for bass |
| Noise | Noise | Pseudo-random, for percussion |
| DMC | Sample | 1-bit delta-modulated samples |
APU registers span $4000-$4017. The audio driver (included automatically when sfx or music declarations exist) runs during NMI.
iNES ROM Format
The standard ROM file format for NES emulators:
Offset Size Description
------ ------- -----------
0 4 bytes Magic number: "NES" followed by $1A
4 1 byte PRG ROM size in 16 KB units
5 1 byte CHR ROM size in 8 KB units
6 1 byte Flags 6: mapper (low nibble), mirroring, battery, trainer
7 1 byte Flags 7: mapper (high nibble), VS/Playchoice, NES 2.0
8-15 8 bytes Padding (zeros)
16+ varies PRG ROM data (N x 16384 bytes)
after varies CHR ROM data (N x 8192 bytes)
Common Mapper Numbers
| Number | Name | PRG ROM | CHR | Notes |
|---|---|---|---|---|
| 0 | NROM | 16/32 KB | 8 KB ROM | No bank switching |
| 1 | MMC1 | Up to 256 KB | Up to 128 KB | Switchable 16 KB PRG banks |
| 2 | UxROM | Up to 256 KB | 8 KB RAM | Switchable 16 KB PRG banks |
| 4 | MMC3 | Up to 512 KB | Up to 256 KB | Scanline counter, 8 KB banks |
Mirroring
- Horizontal: nametables A-A-B-B (vertical scrolling games)
- Vertical: nametables A-B-A-B (horizontal scrolling games)
- Set in byte 6, bit 0 of the iNES header (0 = horizontal, 1 = vertical)
Controller
Each controller has 8 buttons read as a serial shift register via $4016 (port 1) and $4017 (port 2).
Read sequence:
- Write
$01then$00to$4016to strobe (latch button states) - Read
$4016(or$4017) 8 times; bit 0 of each read is one button
Button order: A, B, Select, Start, Up, Down, Left, Right.
The NEScript runtime handles this automatically. The programmer reads button state via button.a, button.up, etc.
Key Constraints for NEScript
| Constraint | Limit | NEScript Response |
|---|---|---|
| Total RAM | 2 KB | Static allocation, no heap |
| Zero page | 256 bytes | fast/slow hints, compiler promotion |
| Stack | 256 bytes | Call depth limit, no recursion |
| Sprites per frame | 64 | Compiler manages OAM buffer |
| Sprites per scanline | 8 | Hardware limit, no workaround |
| Vblank time | ~2,273 cycles | PPU updates must be fast |
| Frame budget | ~29,780 cycles | Frame overrun detection in debug mode |
| No multiply/divide HW | -- | Software routines, power-of-2 optimization |