**The Nintendo Entertainment System (NES)** !!! [**Click here to program the NES in the 8bitworkshop IDE now!**](https://8bitworkshop.com/redir.html?platform=nes) # NES Specifications Lifespan : 1985-1995 (NES), 1983-2003 (Famicom) Media : ROM cartridge (Game Pak) CPU 8-bit : Ricoh 2A03 8-bit processor (MOS Technology 6502 core) @ 1.79 MHz (NTSC) / 1.66 MHz (PAL) Memory : 2K work RAM, 2K video RAM (both can be expanded by cartridge), 256 bytes sprite RAM Controllers : 2 game pad controllers, "Zapper" Best-selling game : Super Mario Bros. ![NES (by Evan-Amos, CC-BY-SA 3.0)](https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/NES-Console-Set.jpg/1024px-NES-Console-Set.jpg) History ======= Following the success of their Game & Watch series and the *Donkey Kong* arcade hit, Nintendo made plans to create their own home game console with interchangable cartridges. Internally dubbed GAMECOM, it was renamed Family Computer (Famicom) based on a suggestion from the wife of Masayuki Uemura, head of Nintendo's R&D2 group. Nintendo almost licensed the ColecoVision wholesale for the Japanese market, due to the success of its *Donkey Kong* port, and their familiarity with the Z80 CPU. But instead they forged ahead with their own design. The personal computer boom had led to a shortage in microprocessors, and few suppliers wanted to take on the risky prospect of a new game console. The manufacturer Ricoh had lots of spare capacity and personal connections with Nintendo engineers. They agreed to help produce custom chips for the new game console, with Nintendo guaranteeing them a three-million chip order. Ricoh suggested the 6502 CPU, which took up less chip space than the Z80. Nintendo was unfamiliar with the 6502, but they surmised that its obscurity would stall competitors hoping to reverse-engineer the console. The Nintendo chip omits the BCD (Binary Coded Decimal) logic for legal reasons, but adds a complex sound generator and a DMA controller. Nintendo approached Atari, who was already prototyping its MARIA console (later sold as the Atari 7800) and looking for an alternate console should its plans fall though. But the video game crash of 1983 would keep the Famicom in Japan for the time being. The Famicom was released on July 15, 1983 for ¥14,800 (equivalent to ¥18,400 in 2019) alongside three ports of Nintendo's successful arcade games *Donkey Kong*, *Donkey Kong Jr.* and *Popeye*. Its "plus controller", now known as a game pad, was lifted straight from the Game & Watch. At June 1985's Consumer Electronics Show (CES), Nintendo unveiled the American version of its Famicom, the NES. It featured a new case and a "zero insertion force" cartridge slot. Nintendo released 17 launch games: *10-Yard Fight, Baseball, Clu Clu Land, Duck Hunt, Excitebike, Golf, Gyromite, Hogan’s Alley, Ice Climber, Kung Fu, Pinball, Soccer, Stack-Up, Super Mario Bros., Tennis, Wild Gunman* and *Wrecking Crew*. For the nationwide release, Nintendo offered the “Action Set” which replaced the gimmicky Robotic Operating Buddy with *Super Mario Bros*. Programming =========== Almost all NES commercial titles were written in 6502 assembly language. This allowed developers to fine-tune each and every byte of their programs. This was critical when fitting games into the small but expensive in the game cartridge. It also allowed for clever optimizations which squeeze the most performance possible out of each frame of animation. Nowadays, homebrew NES developers usually write games in either assembler or C, the latter using the cc65 compiler toolchain. Writing in C gives you more functionality per line of code. While it has lower performance and greater code size than a well-written assembly program, you can still write a pretty good game in C. NES games come in cartridges, and inside of those cartridges are various circuits and hardware. Different games use different circuits and hardware, and the configuration and capabilities of such cartridges is commonly called their *mapper*. Mappers are designed to extend the system and bypass its limitations, such as by adding RAM to the cartridge or even extra sound channels. More commonly though, mappers are designed to allow games larger than 40K to be made. 8bitworkshop's built-in JSNES emulator simulates many of these circuits. # Memory Map Start | End | Description --------|---------|----------------------------- `$0000` | `$00FF` | RAM, zero-page `$0100` | `$01FF` | RAM, CPU stack `$0200` | `$07FF` | RAM, general-purpose `$0800` | `$1FFF` | (mirror of `$0000-$07FF`) `$2000` | `$2007` | PPU registers `$2008` | `$3FFF` | (mirror of `$2000-$2007`) `$4000` | `$400F` | APU registers `$4010` | `$4017` | DMC, joystick, APU registers `$4020` | `$5FFF` | Cartridge (maybe mapper registers) `$6000` | `$7FFF` | Cartridge RAM (maybe battery-backed) `$8000` | `$FFFF` | PRG ROM (maybe bank switched) `$FFFA` | `$FFFB` | NMI vector `$FFFC` | `$FFFD` | Reset vector `$FFFE` | `$FFFF` | BRK vector ## PPU Registers Address | Name | R/W | Description -----------|------------------|--------|------------ `$2000` | `PPU_CTRL` | write | PPU Control 1 `$2001` | `PPU_MASK` | write | PPU Control 2 `$2002` | `PPU_STATUS` | read | PPU Status `$2003` | `OAM_ADDR` | write | OAM Address `$2004` | `OAM_DATA` | read/write | OAM Data `$2005` | `PPU_SCROLL` | write 2x | Background Scroll Position \newline (write X then Y) `$2006` | `PPU_ADDR` | write 2x | PPU Address \newline (write upper then lower) `$2007` | `PPU_DATA` | read/write | PPU Data `$4014` | `OAM_DMA` | write | Sprite Page DMA Transfer ## PPU_CTRL Bits Bit | Mask | Description ----|------|------------------------- 0-1 | 0x3 | Nametable page select \newline (`0=$2000, 1=$2400, 2=$2800, 3=$2c00`) 2 | 0x4 | VRAM address increment (0=1, 1=32) 3 | 0x8 | 8x8 sprite pattern address (`0=$0, 1=$1000`) 4 | 0x10 | Background pattern address (`0=$0, 1=$1000`) 5 | 0x20 | Sprite size (0=8x8, 1=8x16) 6 | 0x40 | Master/slave mode (unused) 7 | 0x80 | NMI enable at VBLANK (0=off, 1=on) ## PPU_MASK Bits Bit | Mask | Description ----|------|------------------------- 0 | 0x1 | Greyscale (0=color, 1=greyscale) 1 | 0x2 | Show background in leftmost 8 pixels 2 | 0x4 | Show sprites in leftmost 8 pixels 3 | 0x8 | Show background 4 | 0x10 | Show sprites 5 | 0x20 | Red emphasis 6 | 0x40 | Green emphasis 7 | 0x80 | Blue emphasis ## Palette RAM Start | End | Description --------|---------|---------------------- `$3F00` | `$3F00` | Screen color `$3F01` | `$3F03` | Background palette 0 `$3F05` | `$3F07` | Background palette 1 `$3F09` | `$3F0B` | Background palette 2 `$3F0D` | `$3F0F` | Background palette 3 `$3F11` | `$3F13` | Sprite palette 0 `$3F15` | `$3F17` | Sprite palette 1 `$3F19` | `$3F1B` | Sprite palette 2 `$3F1D` | `$3F1F` | Sprite palette 3 ## APU Registers Address | Name | Bits | Description --------|---------------|------------|-------------------------------------- `$4000` | `SQ1_VOL` | `ddlcvvvv` | Square wave 1, duty and volume `$4001` | `SQ1_SWEEP` | `epppnsss` | Square wave 1, sweep `$4002` | `SQ1_LO` | `pppppppp` | Square wave 1, period (LSB) `$4003` | `SQ1_HI` | `xxxxxppp` | Square wave 1, period (MSB) and counter load `$4004` | `SQ2_VOL` | `dd..vvvv` | Square wave 2, duty and volume `$4005` | `SQ2_SWEEP` | `epppnsss` | Square wave 2, sweep `$4006` | `SQ2_LO` | `pppppppp` | Square wave 2, period (LSB) `$4007` | `SQ2_HI` | `xxxxxppp` | Square wave 2, period (MSB) and counter load `$4008` | `TRI_LINEAR` | `crrrrrrr` | Triangle wave, control and counter load `$400A` | `TRI_LO` | `pppppppp` | Triangle wave, period (LSB) `$400B` | `TRI_HI` | `xxxxxppp` | Triangle wave, period (MSB) and counter load `$400C` | `NOISE_VOL` | `..lcvvvv` | Noise generator, flags and volume `$400E` | `NOISE_CTRL` | `t...pppp` | Noise generator, tone and period `$400F` | `NOISE_LEN` | `lllll...` | Noise generator, counter load `$4010` | `DMC_FREQ` | `il..rrrr` | DMC: IRQ, flags, and rate `$4011` | `DMC_RAW` | `.xxxxxxx` | DMC: direct load `$4012` | `DMC_START` | `aaaaaaaa` | DMC, waveform start address `$4013` | `DMC_LEN` | `llllllll` | DMC, waveform length `$4015` | `SND_CHN` | `...dnt21` | Sound channel enable `$4017` | `JOY2` | `mi......` | Frame counter mode and IRQ `$4015` | `SND_CHN` | `if.dnt21` | DMC/frame interrupt and status (read) `$4016` | `JOY1` | `...xxxxd` | Joystick 1 (read) `$4017` | `JOY2` | `...xxxxd` | Joystick 2 (read) # Assembler Reference ## nesdefs.dasm `NES_HEADER` *mapper prgbanks chrbanks mirroring* : Emits a iNES cartridge header before the start of program. The `mirroring` parameter can be `NES_MIRR_HORIZ`, `NES_MIRR_VERT`, or `NES_MIRR_QUAD`. `NES_INIT` : Standardized start-up code. `NES_VECTORS` : Emits 6 bytes of CPU vectors at address `$FFFA`. `SLEEP` *n* : Sleep n cycles. `PPU_SETADDR` *addr* : Set the 16-bit `PPU_ADDR` address register. `PPU_SETVALUE` *data* : Send a constant byte to `PPU_DATA`. `SAVE_REGS` : Push A, X, and Y to stack. `RESTORE_REGS` : Pop A, X, and Y from stack. ## nesppu.dasm `ClearRAM` : Clear all RAM, except for last 2 bytes of CPU stack. `WaitSync` : Wait for vertical sync to start. `NextRandom` : Return a random number in A register. `PrevRandom` : Return a random number in A register. `ReadJoypadY` : Return either joypad's bits in A register. Y must be set to 0 or 1. # C Reference ## apu.h Macros and functions for programming the APU sound generator. `apu_init()` : initialize APU with default state Enable an audio channel: ~~~c APU_ENABLE(flags) flags := ENABLE_PULSE0 | ENABLE_PULSE1 | ENABLE_TRIANGLE | ENABLE_NOISE | ENABLE_DMC ~~~ Pulse channels: ~~~c APU_PULSE_DECAY(channel,period,duty,decay,len) APU_PULSE_SUSTAIN(channel,period,duty,vol) APU_PULSE_SET_DECAY(channel,duty,decay) APU_PULSE_SET_VOLUME(channel,duty,vol) APU_PULSE_SWEEP(channel,period,shift,up) APU_PULSE_SWEEP_DISABLE(channel) channel := PULSE_CH0 | PULSE_CH1 duty := DUTY_75 | DUTY_50 | DUTY_25 | DUTY_12 ~~~ Triangle channel: ~~~c APU_TRIANGLE_LENGTH(period,len) APU_TRIANGLE_SUSTAIN(period) ~~~ Noise channel: ~~~c APU_NOISE_SUSTAIN(_period,vol) APU_NOISE_DECAY(_period,_decay,_len) ~~~ ## bcd.h Binary-encoded decimal math. `unsigned int bcd_add(unsigned int a, unsigned int b)` : add two BCD-encoded 16-bit numbers ## neslib.h `pal_all(const char *data)` : set bg and spr palettes, data is 32 bytes array `pal_bg(const char *data)` : set bg palette only, data is 16 bytes array `pal_spr(const char *data)` : set spr palette only, data is 16 bytes array `pal_col(unsigned char index, unsigned char color)` : set a palette entry, index is 0..31 `pal_clear()` : reset palette to $0f `pal_bright(unsigned char bright)` : set virtual bright both for sprites and background, 0 is black, 4 is normal, 8 is white `pal_spr_bright(unsigned char bright)` : set virtual bright for sprites only `pal_bg_bright(unsigned char bright)` : set virtual bright for sprites background only `ppu_wait_nmi()` : wait actual TV frame, 50hz for PAL, 60hz for NTSC `ppu_wait_frame()` : wait virtual frame, it is always 50hz, frame-to-frame in PAL, frameskip in NTSC `ppu_off()` : turn off rendering, nmi still enabled when rendering is disabled `ppu_on_all()` : turn on bg, spr `ppu_on_bg()` : turn on bg only `ppu_on_spr()` : turn on spr only `ppu_mask(unsigned char mask)` : set PPU_MASK directly `unsigned char ppu_system()` : get current video system, 0 for PAL, not 0 for NTSC `unsigned char nesclock()` : Return an 8-bit counter incremented at each vblank `unsigned char get_ppu_ctrl_var()` : get/set the internal ppu ctrl cache var for manual writing `set_ppu_ctrl_var(unsigned char var)` : set PPU control byte `oam_clear()` : clear OAM buffer, all the sprites are hidden `oam_size(unsigned char size)` : set sprite display mode, 0 for 8x8 sprites, 1 for 8x16 sprites `oam_hide_rest(unsigned char sprid)` : hide all remaining sprites from given offset `oam_spr(x, y, chrnum, attr, sprid)` : set sprite in OAM buffer -- chrnum is tile, attr is attribute, sprid is offset in OAM in bytes returns sprid+4, which is offset for a next sprite `oam_meta_spr(x, y, sprid, data)` : set metasprite in OAM buffer -- meta sprite is a const unsigned char array, it contains four bytes per sprite in order x offset, y offset, tile, attribute x=128 is end of a meta sprite returns sprid+4, which is offset for a next sprite `famitone_init(void* music_data)` : initialize the FamiTone system `sfx_init(void* sounds_data)` : initialize the FamiTone SFX system `music_play(unsigned char song)` : play a music in FamiTone format `music_stop()` : stop music `music_pause(unsigned char pause)` : pause and unpause music `sfx_play(unsigned char sound, unsigned char channel)` : play FamiTone sound effect on channel 0..3 `sample_play(unsigned char sample)` : play a DPCM sample, 1..63 `famitone_update()` : call from NMI once per frame `unsigned char pad_poll(unsigned char pad)` : poll controller and return flags like PAD_LEFT etc, input is pad number (0 or 1) `unsigned char pad_trigger(unsigned char pad)` : poll controller in trigger mode, a flag is set only on button down, not hold if you need to poll the pad in both normal and trigger mode, poll it in the trigger mode for first, then use pad_state `unsigned char pad_state(unsigned char pad)` : get previous pad state without polling ports `scroll(unsigned int x, unsigned int y)` : set scroll, including the top bits it is always applied at beginning of a TV frame, not at the function call `split(unsigned int x, unsigned int y)` : set scroll after screen split invoked by the sprite 0 hit warning: all CPU time between the function call and the actual split point will be wasted! warning: the program loop has to fit into the frame time, ppu_wait_frame should not be used otherwise empty frames without split will be inserted, resulting in jumpy screen warning: only X scroll could be changed in this version `splitxy(unsigned int x, unsigned int y)` : set scroll after screen split invoked by the sprite 0 hit sets both X and Y, but timing might be iffy depending on exact sprite 0 position `bank_spr(unsigned char n)` : select current chr bank for sprites, 0..1 `bank_bg(unsigned char n)` : select current chr bank for background, 0..1 `set_rand(unsigned int seed)` : get random number 0..255 or 0..65535 set random seed `vram_adr(unsigned int adr)` : set vram pointer to write operations if you need to write some data to vram `vram_put(unsigned char n)` : put a byte at current vram address, works only when rendering is turned off `vram_fill(unsigned char n, unsigned int len)` : fill a block with a byte at current vram address, works only when rendering is turned off `vram_inc(unsigned char n)` : set vram autoincrement, 0 for +1 and not 0 for +32 `vram_read(unsigned char *dst, unsigned int size)` : read a block from current address of vram, works only when rendering is turned off `vram_write(const unsigned char *src, unsigned int size)` : write a block to current address of vram, works only when rendering is turned off `vram_unrle(const unsigned char *data)` : unpack RLE data to current address of vram, mostly used for nametables `vram_unlz4(const unsigned char *in, unsigned char *out, const unsigned uncompressed_size)` : unpack LZ4 data to this address `memfill(*dst, unsigned char value, unsigned int len)` : like memset, but does not return anything `delay(unsigned char frames)` : delay for N frames `oam_clear_fast()` : clear OAM memory `oam_meta_spr_pal(unsigned char x,unsigned char y,unsigned char pal,const unsigned char *metasprite)` : set metasprite with palette color `oam_meta_spr_clip(signed int x,unsigned char y,const unsigned char *metasprite)` : set metasprite with clipping `nmi_set_callback(void (*callback)(void))` : set NMI interrupt callback function Gamepad flags: : `PAD_A` `PAD_B` `PAD_SELECT` `PAD_START` `PAD_UP` `PAD_DOWN` `PAD_LEFT` `PAD_RIGHT` OAM flags: : `OAM_FLIP_V` `OAM_FLIP_H` `OAM_BEHIND` `MAX(x1,x2)` : get maximum of two integers `MIN(x1,x2)` : get minimum of two integers Mask flags: : `MASK_SPR` `MASK_BG` `MASK_EDGE_SPR` `MASK_EDGE_BG` `MASK_TINT_RED` `MASK_TINT_BLUE` `MASK_TINT_GREEN` `MASK_MONO` Nametable constants: : `NAMETABLE_A` `NAMETABLE_B` `NAMETABLE_C` `NAMETABLE_D` `NTADR_A(x,y)` : macro to calculate nametable address from X,Y in compile time `NTADR_B(x,y)` : macro to calculate nametable address from X,Y in compile time `NTADR_C(x,y)` : macro to calculate nametable address from X,Y in compile time `NTADR_D(x,y)` : macro to calculate nametable address from X,Y in compile time `MSB(x)` : macro to get MSB (high byte) `LSB(x)` : macro to get LSB (low byte) `OAMBUF` : OAM buffer @ $200-$2FF `oam_off` : internal OAM offset variable ### VRAM Update Buffer `set_vram_update(unsigned char *buf)` : The function sets a pointer to the update buffer that contains data and addresses in a special format. It allows to write non-sequental bytes, as well as horizontal or vertical nametable sequences. Buffer pointer could be changed during rendering, but it only takes effect on a new frame. Number of transferred bytes is limited by vblank time. To disable updates, call this function with NULL pointer. `flush_vram_update(unsigned char *buf)` : The same format as for set_vram_update, but writes done right away, instead of waiting for NMI interrupt. The update data format: * `MSB, LSB, byte` for a non-sequental write * `MSB|NT_UPD_HORZ, LSB, LEN, [bytes]` for a horizontal sequence * `MSB|NT_UPD_VERT, LSB, LEN, [bytes]` for a vertical sequence * `NT_UPD_EOF` to mark end of the buffer ## vrambuf.h `VBUFSIZE` : maximum update buffer bytes `updbuf` : update buffer starts at $100 (stack page) `VRAMBUF_SET(byte)` : set byte at end of buffer (do not advance pointer) `VRAMBUF_ADD(byte)` : add byte to end of buffer `VRAMBUF_PUT(addr,len,flags)` : macro to add a raw header (useful for single bytes) `VRAMBUF_VERT` : OR with address to put vertical run `vrambuf_end()` : add EOF marker to buffer (but don't increment pointer) `vrambuf_clear()` : clear vram buffer and place EOF marker `vrambuf_flush()` : wait for next frame, then clear buffer this assumes the NMI will call flush_vram_update() `vrambuf_put(word addr, const char* str, byte len)` : add multiple characters to update buffer using horizontal increment # Helpful Links * [cc65 function reference](https://www.cc65.org/doc/funcref.html) * [Browse 8bitworkshop NES Projects](https://8bitworkshop.com/projects/?i=nes) !!! [**Click here to program the NES in the 8bitworkshop IDE now!**](https://8bitworkshop.com/redir.html?platform=nes)