The current URL is datacrystal.tcrf.net.
Nintendo Entertainment System
Nintendo Entertainment System | |
Manufacturer | Nintendo |
CPU | Ricoh RP2A03 (1.97mhz) |
GPU | Ricoh RP2C02 PPU |
RAM | 2 KiB |
VRAM | 2 KiB |
SRAM | |
Media | |
Connectivity | |
Players supported | 2
4 (with NES Four Score) |
The Nintendo Entertainment System, or NES, was a video game console based around the NMOS Technology 6502. The custom chip was produced by Ricoh, and named the RP2A03 processor. The NES utilized ROM cartridges for gameplay.
Games
Hardware
Hardware information |
Emulators
The most useful NES emulator for hacking purposes is FCEUXD SP which includes a Trace Logger, a built-in Hex Editor, a Name Table Viewer, Code/Data Logger, Inline Assembler, and Game Genie Decoder/Encoder, PPU Viewer, and a Debugger with Conditional Breakpoints and Symbolic Debugging.
ROM Images
NES ROM images generally have the extension .NES and are organized according to the iNES format. Another format, UNIF (Universal NES Interchange Format), hopes to replace iNES.
Copiers & Flash Cartridges
- Schematics of an old copier by Marat Fayzullin are available here.
- The IO-56 is a homemade NES dumper by Pascal Felber.
- ChameleonNES is a home made USB NES dumper by team Knox. It is based on the Chameleon-USB by Optimize (Japanese only). Unfortunately, ChameleonNES can only dump cartridges without a memory mapper.
- The NES Funky Flash Cart is a home made USB prototype flash cartridge. It has somewhat limited ROM support, but its compatibility will increase as the author adds more support for different mappers. More info can be found here.
Data Formats
Nintendo Display List (Stripe) Format
A common format used by Nintendo development groups such as the R&Ds, Intelligent Systems, SRD, and to a degree, HAL, the display list format known as the Nintendo "stripe" format consists of a list of individual blocks of data to write into the PPU address space, typically during the vertical (non-maskable) interrupt. The blocks essentially comprise a run-length-encoded PPU data payload with arbitrary placement of individual strings in the buffer. Variations of the format have appeared over the years, with the common elements being a block format consisting of a big-endian PPU address, followed by a length descriptor, then a data payload. After a payload matching the length descriptor, generally another address is expected or a series of special bytes. All formats support a terminator byte, which is variably 0 or negative depending on the implementation. Generally Famicom titles use 0, the FDS boot ROM version of this routine uses $FF typically although any block starting byte with the sign bit set will act as a terminator in this case.
An extended version, which first appeared in Clu Clu Land and then, with modifications, in the FDS boot ROM, includes the ability to branch into "sublists" and return from them. In practice, the 6502 "jmp" ($4C) and "rts" ($60) opcodes are used to jump to and return from shorter chunks of display list information. In reality, the jmp represents a jsr because the return address is pushed, but jsr is encoded as $20 on the 6502, overlapping with a legitimate part of the PPU address map.
In any case, the display block format is represented by the following C-like structure:
struct ndl_dblock { char *ppu_addr; char len_desc; char payload[]; };
Ignoring the special bytes, the address field is taken as the PPU VRAM address that this specific block in the list should be written to. Generally this is the nametable coordinate of a tile to update or color RAM address to write palette bytes at. This format might also allow writing to CHR-RAM but has not been observed as of this writing.
The length descriptor is a payload size along with a few special high bits. If bit 7 is set, then the VRAM address is incremented 32-bytes rather than-byte between writes. This is done by setting the increment mode in the PPU control register. Effectively this results in row- rather than column-based increment when writing nametables. Bit 6 is used for run-length encoding, indicating to emit a run of a single payload character rather than multiple. The remainder of the length descriptor is the number of elements to write, capped at 32-per-block since the first two bits are occupied by the row toggle and RLE bits. An alternate version of the display list algorithm is given in Gomoku no Renji and possibly others that includes logic to write multiple iterations of the same display block over and over again based on another iteration count byte. This version is not seen in other Nintendo titles at least within the first few years of the console.
Following the length descriptor is either a string of bytes of that length or a single byte in the case of an RLE run. Since this format encodes nametables and nametables in turn are often used with character glyphs for text, display lists when used are often where game text is loaded and stored.
For the record, many games that use this format include routines to stream bytes into display lists from various sources and then provide the length/RLE/increment characteristics dynamically by setting the bits at runtime. Drawing requests for various strings are then requested by setting a special byte in memory that, on the next vertical interrupt, draws the corresponding display list from a table, with at least one buffer in memory supplied for dynamic updates. This dynamic table includes an offset indicating where the current head of the full table is. Additionally, empty entries are typically filled with the terminator byte such that the head of any given write request is writing to a terminator written at the end of the previous request. While not essential, this consumption of the display list format and core subroutine is the most typical observed.
Other titles besides those mentioned also make their own slight adjustments to the display list routine, but this information should cover the base interface and most common extensions. Consult FDS-specific documentation for more detail on the public interface of the boot ROM routine.
Nintendo Linear-Feedback Shift Register RNG
Seen in many Nintendo titles as well as the FDS boot ROM is a linear-feedback shift register mechanism used as a source of entropy for game logic and other operations. The general format of the algorithm is given by:
SRAND_ENTROPY = %00000010 .export lib_rand lib_rand: lda srand+0 and #SRAND_ENTROPY sta srand_buffer lda srand+1 and #SRAND_ENTROPY eor srand_buffer ; if (((srand[0] ^ srand[1]) & SRAND_ENTROPY)) { clc beq :+ ; } else { sec : ; } .repeat SRAND_REGISTER_SIZE, i ror srand+i .endrep rts
Where "srand" is the shift register. Bit 1 of the first two bytes of the shift register then feed back into the head of the register. User code then simply samples bytes from the shift register, with different bytes typically being assigned then to different functions.
Several variations on the algorithm exist, with some ditching linear-feedback shifting entirely or using different combinations of bits and functions on those bits for the linear feedback. This version is included in the FDS boot ROM with a slight change in interface to allow defining externally the location and size of the shift register in the low page of 6502 memory. Typically these are provided as constant pointers in-situ. Many titles observed roll RNG on a tight loop in the main thread while all other execution happens one frame at a time in the vertical interrupt. However, several strategies on RNG recycling have been observed and indeed a roll of RNG can be performed synchronously at any time. Consult FDS boot ROM documentation for the provided ABI.
Nintendo Tiered Timers
A common pattern in timer ticking observed in Nintendo titles is the use of two sets of timers, one ticked on every frame, the other ticked only after a set number of frames has been observed. The general format of the algorithm is given by:
TIMER_LONG_DELAY = 11 .export timers_tick timers_tick: ldx #(timers_end_short-(timers+1))-1 dec timers+0 bpl :+ ; if (--timer[0] < 0) { lda #TIMER_LONG_DELAY-1 sta timers+0 ldx #(timers_end-(timers+1))-1 : ; } ; for (timer of (timers+1)) { lda timers+1, x beq :+ ; if (timer > 0) { dec timers+1, x : ; } dex bpl :-- ; } rts
So a bank of timers is provided, with the very first being used to indicate what part of that bank will be updated. If this first timer has not ticked down on this tick, then the list of timers ticked is ended prematurely. Otherwise, the full list of timers is ticked this round and the master timer is reset. In effect, this results in the second set of timers only ticking when this timer resets. For most observed cases, this long timer delay occurs every 11 ticks. In this case, a tick is a "call" of the timer routine. More often than not, this is called once per frame, in the same logic pathway as the frame counter is incremented. Some titles incorporate this logic directly into their vertical interrupt handlers, while others may change the long timer delay. The FDS boot ROM provides a version of this routine with configurable timer bank placement and dimensions. FDS boot ROM documentation should be consulted for the ABI.
Nintendo | |
---|---|
Consoles | NES • SNES • Wii |
Handhelds | Game Boy • Game Boy Advance • DS • 3DS • Pokémon mini |
Official Accessories | BS-X |
Sega | |
Consoles | Master System • Genesis • Game Gear |
SNK | |
Consoles | Neo Geo • Neo Geo Pocket |
Sony | |
Consoles | PlayStation |
Handhelds | PlayStation Portable |
Microsoft | |
Consoles | Xbox 360 |
Others | |
Bandai | WonderSwan |
Capcom | Play System 2 |
NEC | TurboGrafx-16 • PC-8801 • PC-9801 |
Sharp | Sharp X68000 |