Gameboy Advance / Nintendo DS / DSi / 3DS - Technical Info - Extracted from no$gba version 3.04
ARM Mode ARM7TDMI 32bit RISC CPU, 16.78MHz, 32bit opcodes (GBA)
THUMB Mode ARM7TDMI 32bit RISC CPU, 16.78MHz, 16bit opcodes (GBA)
CGB Mode Z80/8080-style 8bit CPU, 4.2MHz or 8.4MHz (CGB compatibility)
DMG Mode Z80/8080-style 8bit CPU, 4.2MHz (monochrome gameboy compatib.)
BIOS ROM 16 KBytes
Work RAM 288 KBytes (Fast 32K on-chip, plus Slow 256K on-board)
VRAM 96 KBytes
OAM 1 KByte (128 OBJs 3x16bit, 32 OBJ-Rotation/Scalings 4x16bit)
Palette RAM 1 KByte (256 BG colors, 256 OBJ colors)
Display 240x160 pixels (2.9 inch TFT color LCD display)
BG layers 4 background layers
BG types Tile/map based, or Bitmap based
BG colors 256 colors, or 16 colors/16 palettes, or 32768 colors
OBJ colors 256 colors, or 16 colors/16 palettes
OBJ size 12 types (in range 8x8 up to 64x64 dots)
OBJs/Screen max. 128 OBJs of any size (up to 64x64 dots each)
OBJs/Line max. 128 OBJs of 8x8 dots size (under best circumstances)
Priorities OBJ/OBJ: 0-127, OBJ/BG: 0-3, BG/BG: 0-3
Effects Rotation/Scaling, alpha blending, fade-in/out, mosaic, window
Backlight GBA SP only (optionally by light on/off toggle button)
Analogue 4 channel CGB compatible (3x square wave, 1x noise)
Digital 2 DMA sound channels
Output Built-in speaker (mono), or headphones socket (stereo)
Gamepad 4 Direction Keys, 6 Buttons
Serial Port Various transfer modes, 4-Player Link, Single Game Pak play
GBA Game Pak max. 32MB ROM or flash ROM + max 64K SRAM
CGB Game Pak max. 32KB ROM + 8KB SRAM (more memory requires banking)
Size (mm) GBA: 145x81x25 - GBA SP: 82x82x24 (closed), 155x82x24 (stretch)
Battery GBA GBA: 2x1.5V DC (AA), Life-time approx. 15 hours
Battery SP GBA SP: Built-in rechargeable Lithium ion battery, 3.7V 600mAh
External GBA: 3.3V DC 350mA - GBA SP: 5.2V DC 320mA
____._____________...___.____
____/ : CARTRIDGE SIO : \____
| L _____________________ LED R |
| | | |
| _||_ | 2.9" TFT SCREEN | (A) |
| |_ _| | 240x160pix 61x40mm | (B) |
| || | NO BACKLIGHT | :::: |
| | | SPEAKR |
| STRT() |_____________________| :::: |
| SLCT() GAME BOY ADVANCE VOLUME |
|____ OFF-ON BATTERY 2xAA PHONES _==_|
\__.##.__________________,,___/
_______________________ _
| _____________________ | / /
|| || / /
|| 2.9" TFT SCREEN || / /
|| 240x160pix 61x40mm || / /
|| WITH BACKLIGHT || / /
|| || GBA SP SIDE VIEWS / /
||_____________________|| / /
| GAME BOY ADVANCE SP | _____________________(_)
|_______________________| |. . . . . . . .'.'. _|
|_|________|________|_|_| |_CARTRIDGE_:_BATT._:_|_| <-- EXT1/EXT2
|L EXT1 EXT2 R|
| (*) LEDSo _____________________ _
(VOL_||_ (A) o |_____________________(_)
| |_ _| ,,,,,(B) | |. . . . . . . .'.'. _|
| || ;SPK; | |_CARTRIDGE_:_BATT._:_|_| <-- EXT1/EXT2
| ''''' ON # _ _____________________
| SLCT STRT OFF# _____________________(_)_____________________|
| CART. () () | |. . . . . . . .'.'. _|
|_:___________________:_| |_CARTRIDGE_:_BATT._:_|_| <-- EXT1/EXT2
________________SIO_______________
| L __________________ R |
| | GBA-MICRO | |
| _||_ | 2.0" TFT SCREEN | (A)| +
||_ _| |240x160pix 42x28mm| (B) |VOL
| || | BACKLIGHT | | -
| |__________________| ... |
|___________SELECT__START__________|
PWR <--- CARTRIDGE SLOT ---> PHONES
_____________________________________
| _____________________ |
| | | |
| | 3" TFT SCREEN | |
| | 256x192pix 61x46mm | |
| | BACKLIGHT | |
| ::::: | Original NDS | ::::: |
| ::::: |_____________________| ::::: |
_| _ ______ _ |_ <-- gap between screens: 22mm
|L|_______| |________| |_| |_______|R| (equivalent to 90 pixels)
|_______ _____________________ _______|
| PWR | | | |SEL STA|
| _ | | 3" TFT SCREEN | | |
| _| |_ | | 256x192pix 61x46mm | | X |
||_ _|| | BACKLIGHT | | Y A |
| |_| | | TOUCH SCREEN | | B |
| | |_____________________| | |
|_______| NintendoDS |_______|
| MIC LEDS |
|_________________________________________|
VOL SLOT2(GBA) MIC/PHONES
_____________________________________
| _____________________ |
| | | |
| | 3" TFT SCREEN | |
| ... | 256x192pix 61x46mm | ... |
| ... | BACKLIGHT | ... |
| | NDS-LITE | |
| |_____________________| |
|___ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____| <-- gap between screens: 23mm
L| _ |_____________MIC____________|LEDS|R
| _ _____________________ |
| _| |_ | | X |
||_ _|| 3" TFT SCREEN | Y A |PWR
| |_| | 256x192pix 61x46mm | B |
| | BACKLIGHT | |
| | TOUCH SCREEN |oSTART |
| |_____________________|oSELECT|
|_____________________________________|
VOL SLOT2(GBA) MIC/PHONES
_____________________________________
| _____________________ |
| | | O o | <-- CAM (O) and LED (o)
| | 3.25" TFT SCREEN | | (on backside)
| | 256x192pix 66x50mm | |
| | BACKLIGHT | |
| __ | DSi | __ |
| (__) |_____________________| (__) |
|___ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____| <-- gap between screens: 23mm
L|LEDS|__________CAM__MIC_________| __ |R (88 pixels)
+ | _ _____________________ |
VOL| _| |_ | | X | <-- SD Card Slot
- ||_ _|| 3.25" TFT SCREEN | Y A |
| |_| | 256x192pix 66x50mm | B |
| | BACKLIGHT | |
| | TOUCH SCREEN |oSTART |
| POWERo|_____________________|oSELECT|
|_____________________________________|
MIC/PHONES
As DSi, but bigger case, and bigger 4.2" screens
_________
L____------- -------____R
/ ___ \ / (Y) \Z
/ / O \ | (START) | (X)\ Z = Gameboy Player Menu
| \___/ \_______/ (A) | X or Y = Select button
|\ _ \ / (B) /|
| \___ _| |_ \ / ___ ___/ | optionally X/Y can be
| |\ |_ _| / \ / C \ /| | swapped with L/R (?)
| | \ |_| / \ \___/ / | |
| | \_____/ \_____/ | | analogue sticks = ?
\__/ \__/
_______ _______
/ Y \ / X \ Y/B = left bongo rear/front side
| . . . . |_| . . . . | X/A = right bongo rear/front side
| B |R| A | S = start/pause button
|\_______/|_|\_______/| R = microphone (triggers R button)
|\_______/|S|\_______/|
| |_| | (the X/Y inputs can be assigned to
|\_______/| |\_______/| GBA R/L inputs in GBA player setup)
\_______/ \_______/
The GBA’s separate 8bit/32bit CPU modes cannot be operated simultaneously. Switching is allowed between ARM and THUMB modes only (that are the two GBA modes).
This manual does not describe CGB and DMG modes, both are completely different than GBA modes, and both cannot be accessed from inside of GBA modes anyways.
An GBA Adapter for the Gamecube console; allowing to play GBA games on a television set.
Deluxe version of the original GBA. With backlight, new folded laptop-style case, and built-in rechargeable battery. Appears to be 100% compatible with GBA, there seems to be no way to detect SPs by software.
Minituarized GBA. Supports 32bit GBA games only (no 8bit DMG/CGB games). The 256K Main RAM is a bit slower than usually (cannot be “overclocked via port 4000800h).
New handheld with two screens, backwards compatible with GBA games, it is NOT backwards compatible with older 8bit games (mono/color gameboys) though..
Also, the DS has no link port, so that GBA games will thus work only in single player mode, link-port accessoires like printers cannot be used, and most unfortunately multiboot won’t work (trying to press Select+Start at powerup will just lock up the DS).
iQue is a brand name used by Nintendo in China, iQue GBA and iQue DS are essentially same as Nintendo GBA and Nintendo DS.
The iQue DS contains a larger firmware chip (the charset additionally contains about 6700 simplified chinese characters), the bootmenu still allows to select (only) six languages (japanese has been replaced by chinese). The iQue DS can play normal international NDS games, plus chinese dedicated games. The latter ones won’t work on normal NDS consoles (that, reportedly simply due to a firmware-version check contained in chinese dedicated games, aside from that check, the games should be fully compatible with NDS consoles).
00000000-00003FFF BIOS - System ROM (16 KBytes)
00004000-01FFFFFF Not used
02000000-0203FFFF WRAM - On-board Work RAM (256 KBytes) 2 Wait
02040000-02FFFFFF Not used
03000000-03007FFF WRAM - On-chip Work RAM (32 KBytes)
03008000-03FFFFFF Not used
04000000-040003FE I/O Registers
04000400-04FFFFFF Not used
05000000-050003FF BG/OBJ Palette RAM (1 Kbyte)
05000400-05FFFFFF Not used
06000000-06017FFF VRAM - Video RAM (96 KBytes)
06018000-06FFFFFF Not used
07000000-070003FF OAM - OBJ Attributes (1 Kbyte)
07000400-07FFFFFF Not used
08000000-09FFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 0
0A000000-0BFFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 1
0C000000-0DFFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 2
0E000000-0E00FFFF Game Pak SRAM (max 64 KBytes) - 8bit Bus width
0E010000-0FFFFFFF Not used
10000000-FFFFFFFF Not used (upper 4bits of address bus unused)
By default, the 256 bytes at 03007F00h-03007FFFh in Work RAM are reserved for Interrupt vector, Interrupt Stack, and BIOS Call Stack. The remaining WRAM is free for whatever use (including User Stack, which is initially located at 03007F00h).
Shows the Bus-Width, supported read and write widths, and the clock cycles for 8/16/32bit accesses.
Region Bus Read Write Cycles
BIOS ROM 32 8/16/32 - 1/1/1
Work RAM 32K 32 8/16/32 8/16/32 1/1/1
I/O 32 8/16/32 8/16/32 1/1/1
OAM 32 8/16/32 16/32 1/1/1 *
Work RAM 256K 16 8/16/32 8/16/32 3/3/6 **
Palette RAM 16 8/16/32 16/32 1/1/2 *
VRAM 16 8/16/32 16/32 1/1/2 *
GamePak ROM 16 8/16/32 - 5/5/8 **/***
GamePak Flash 16 8/16/32 16/32 5/5/8 **/***
GamePak SRAM 8 8 8 5 **
Timing Notes:
* Plus 1 cycle if GBA accesses video memory at the same time.
** Default waitstate settings, see System Control chapter.
*** Separate timings for sequential, and non-sequential accesses.
One cycle equals approx. 59.59ns (ie. 16.78MHz clock).
All memory (except GamePak SRAM) can be accessed by 16bit and 32bit DMA.
Only DMA3 (and the CPU of course) may access GamePak ROM. GamePak SRAM can be accessed by the CPU only - restricted to bytewise 8bit transfers. The SRAM region is supposed for as external FLASH backup memory, or for battery-backed SRAM.
For details about configuration of GamePak Waitstates, see:
These memory regions can be accessed during H-Blank or V-Blank only (unless display is disabled by Forced Blank bit in DISPCNT register).
There is an additional restriction for OAM memory: Accesses during H-Blank are allowed only if ‘H-Blank Interval Free’ in DISPCNT is set (which’d reduce number of display-able OBJs though).
The CPU appears to be able to access VRAM/OAM/Palette at any time, a waitstate (one clock cycle) being inserted automatically in case that the display controller was accessing memory simultaneously. (Ie. unlike as in old 8bit gameboy, the data will not get lost.)
Note that the GamePak ROM bus is limited to 16bits, thus executing ARM instructions (32bit opcodes) from inside of GamePak ROM would result in a not so good performance. So, it’d be more recommended to use THUMB instruction (16bit opcodes) which’d allow each opcode to be read at once.
(ARM instructions can be used at best performance by copying code from GamePak ROM into internal Work RAM)
Even though the ARM CPU itself would allow to select between Little-Endian and Big-Endian format by using an external circuit, in the GBA no such circuit exists, and the data format is always Little-Endian. That is, when accessing 16bit or 32bit data in memory, the least significant bits are stored in the first byte (smallest address), and the most significant bits in the last byte. (Ie. same as for 80x86 and Z80 CPUs.)
4000000h 2 R/W DISPCNT LCD Control
4000002h 2 R/W - Undocumented - Green Swap
4000004h 2 R/W DISPSTAT General LCD Status (STAT,LYC)
4000006h 2 R VCOUNT Vertical Counter (LY)
4000008h 2 R/W BG0CNT BG0 Control
400000Ah 2 R/W BG1CNT BG1 Control
400000Ch 2 R/W BG2CNT BG2 Control
400000Eh 2 R/W BG3CNT BG3 Control
4000010h 2 W BG0HOFS BG0 X-Offset
4000012h 2 W BG0VOFS BG0 Y-Offset
4000014h 2 W BG1HOFS BG1 X-Offset
4000016h 2 W BG1VOFS BG1 Y-Offset
4000018h 2 W BG2HOFS BG2 X-Offset
400001Ah 2 W BG2VOFS BG2 Y-Offset
400001Ch 2 W BG3HOFS BG3 X-Offset
400001Eh 2 W BG3VOFS BG3 Y-Offset
4000020h 2 W BG2PA BG2 Rotation/Scaling Parameter A (dx)
4000022h 2 W BG2PB BG2 Rotation/Scaling Parameter B (dmx)
4000024h 2 W BG2PC BG2 Rotation/Scaling Parameter C (dy)
4000026h 2 W BG2PD BG2 Rotation/Scaling Parameter D (dmy)
4000028h 4 W BG2X BG2 Reference Point X-Coordinate
400002Ch 4 W BG2Y BG2 Reference Point Y-Coordinate
4000030h 2 W BG3PA BG3 Rotation/Scaling Parameter A (dx)
4000032h 2 W BG3PB BG3 Rotation/Scaling Parameter B (dmx)
4000034h 2 W BG3PC BG3 Rotation/Scaling Parameter C (dy)
4000036h 2 W BG3PD BG3 Rotation/Scaling Parameter D (dmy)
4000038h 4 W BG3X BG3 Reference Point X-Coordinate
400003Ch 4 W BG3Y BG3 Reference Point Y-Coordinate
4000040h 2 W WIN0H Window 0 Horizontal Dimensions
4000042h 2 W WIN1H Window 1 Horizontal Dimensions
4000044h 2 W WIN0V Window 0 Vertical Dimensions
4000046h 2 W WIN1V Window 1 Vertical Dimensions
4000048h 2 R/W WININ Inside of Window 0 and 1
400004Ah 2 R/W WINOUT Inside of OBJ Window & Outside of Windows
400004Ch 2 W MOSAIC Mosaic Size
400004Eh - - Not used
4000050h 2 R/W BLDCNT Color Special Effects Selection
4000052h 2 R/W BLDALPHA Alpha Blending Coefficients
4000054h 2 W BLDY Brightness (Fade-In/Out) Coefficient
4000056h - - Not used
4000060h 2 R/W SOUND1CNT_L Channel 1 Sweep register (NR10)
4000062h 2 R/W SOUND1CNT_H Channel 1 Duty/Length/Envelope (NR11, NR12)
4000064h 2 R/W SOUND1CNT_X Channel 1 Frequency/Control (NR13, NR14)
4000066h - - Not used
4000068h 2 R/W SOUND2CNT_L Channel 2 Duty/Length/Envelope (NR21, NR22)
400006Ah - - Not used
400006Ch 2 R/W SOUND2CNT_H Channel 2 Frequency/Control (NR23, NR24)
400006Eh - - Not used
4000070h 2 R/W SOUND3CNT_L Channel 3 Stop/Wave RAM select (NR30)
4000072h 2 R/W SOUND3CNT_H Channel 3 Length/Volume (NR31, NR32)
4000074h 2 R/W SOUND3CNT_X Channel 3 Frequency/Control (NR33, NR34)
4000076h - - Not used
4000078h 2 R/W SOUND4CNT_L Channel 4 Length/Envelope (NR41, NR42)
400007Ah - - Not used
400007Ch 2 R/W SOUND4CNT_H Channel 4 Frequency/Control (NR43, NR44)
400007Eh - - Not used
4000080h 2 R/W SOUNDCNT_L Control Stereo/Volume/Enable (NR50, NR51)
4000082h 2 R/W SOUNDCNT_H Control Mixing/DMA Control
4000084h 2 R/W SOUNDCNT_X Control Sound on/off (NR52)
4000086h - - Not used
4000088h 2 BIOS SOUNDBIAS Sound PWM Control
400008Ah .. - - Not used
4000090h 2x10h R/W WAVE_RAM Channel 3 Wave Pattern RAM (2 banks!!)
40000A0h 4 W FIFO_A Channel A FIFO, Data 0-3
40000A4h 4 W FIFO_B Channel B FIFO, Data 0-3
40000A8h - - Not used
40000B0h 4 W DMA0SAD DMA 0 Source Address
40000B4h 4 W DMA0DAD DMA 0 Destination Address
40000B8h 2 W DMA0CNT_L DMA 0 Word Count
40000BAh 2 R/W DMA0CNT_H DMA 0 Control
40000BCh 4 W DMA1SAD DMA 1 Source Address
40000C0h 4 W DMA1DAD DMA 1 Destination Address
40000C4h 2 W DMA1CNT_L DMA 1 Word Count
40000C6h 2 R/W DMA1CNT_H DMA 1 Control
40000C8h 4 W DMA2SAD DMA 2 Source Address
40000CCh 4 W DMA2DAD DMA 2 Destination Address
40000D0h 2 W DMA2CNT_L DMA 2 Word Count
40000D2h 2 R/W DMA2CNT_H DMA 2 Control
40000D4h 4 W DMA3SAD DMA 3 Source Address
40000D8h 4 W DMA3DAD DMA 3 Destination Address
40000DCh 2 W DMA3CNT_L DMA 3 Word Count
40000DEh 2 R/W DMA3CNT_H DMA 3 Control
40000E0h - - Not used
4000100h 2 R/W TM0CNT_L Timer 0 Counter/Reload
4000102h 2 R/W TM0CNT_H Timer 0 Control
4000104h 2 R/W TM1CNT_L Timer 1 Counter/Reload
4000106h 2 R/W TM1CNT_H Timer 1 Control
4000108h 2 R/W TM2CNT_L Timer 2 Counter/Reload
400010Ah 2 R/W TM2CNT_H Timer 2 Control
400010Ch 2 R/W TM3CNT_L Timer 3 Counter/Reload
400010Eh 2 R/W TM3CNT_H Timer 3 Control
4000110h - - Not used
4000120h 4 R/W SIODATA32 SIO Data (Normal-32bit Mode; shared with below)
4000120h 2 R/W SIOMULTI0 SIO Data 0 (Parent) (Multi-Player Mode)
4000122h 2 R/W SIOMULTI1 SIO Data 1 (1st Child) (Multi-Player Mode)
4000124h 2 R/W SIOMULTI2 SIO Data 2 (2nd Child) (Multi-Player Mode)
4000126h 2 R/W SIOMULTI3 SIO Data 3 (3rd Child) (Multi-Player Mode)
4000128h 2 R/W SIOCNT SIO Control Register
400012Ah 2 R/W SIOMLT_SEND SIO Data (Local of MultiPlayer; shared below)
400012Ah 2 R/W SIODATA8 SIO Data (Normal-8bit and UART Mode)
400012Ch - - Not used
4000130h 2 R KEYINPUT Key Status
4000132h 2 R/W KEYCNT Key Interrupt Control
4000134h 2 R/W RCNT SIO Mode Select/General Purpose Data
4000136h - - IR Ancient - Infrared Register (Prototypes only)
4000138h - - Not used
4000140h 2 R/W JOYCNT SIO JOY Bus Control
4000142h - - Not used
4000150h 4 R/W JOY_RECV SIO JOY Bus Receive Data
4000154h 4 R/W JOY_TRANS SIO JOY Bus Transmit Data
4000158h 2 R/? JOYSTAT SIO JOY Bus Receive Status
400015Ah - - Not used
4000200h 2 R/W IE Interrupt Enable Register
4000202h 2 R/W IF Interrupt Request Flags / IRQ Acknowledge
4000204h 2 R/W WAITCNT Game Pak Waitstate Control
4000206h - - Not used
4000208h 2 R/W IME Interrupt Master Enable Register
400020Ah - - Not used
4000300h 1 R/W POSTFLG Undocumented - Post Boot Flag
4000301h 1 W HALTCNT Undocumented - Power Down Control
4000302h - - Not used
4000410h ? ? ? Undocumented - Purpose Unknown / Bug ??? 0FFh
4000411h - - Not used
4000800h 4 R/W ? Undocumented - Internal Memory Control (R/W)
4000804h - - Not used
4xx0800h 4 R/W ? Mirrors of 4000800h (repeated each 64K)
4700000h 4 W (3DS) Disable ARM7 bootrom overlay (3DS only)
All further addresses at 4XXXXXXh are unused and do not contain mirrors of the I/O area, with the only exception that 4000800h is repeated each 64K (ie. mirrored at 4010800h, 4020800h, etc.)
Bit Expl.
0-2 BG Mode (0-5=Video Mode 0-5, 6-7=Prohibited)
3 Reserved / CGB Mode (0=GBA, 1=CGB; can be set only by BIOS opcodes)
4 Display Frame Select (0-1=Frame 0-1) (for BG Modes 4,5 only)
5 H-Blank Interval Free (1=Allow access to OAM during H-Blank)
6 OBJ Character VRAM Mapping (0=Two dimensional, 1=One dimensional)
7 Forced Blank (1=Allow FAST access to VRAM,Palette,OAM)
8 Screen Display BG0 (0=Off, 1=On)
9 Screen Display BG1 (0=Off, 1=On)
10 Screen Display BG2 (0=Off, 1=On)
11 Screen Display BG3 (0=Off, 1=On)
12 Screen Display OBJ (0=Off, 1=On)
13 Window 0 Display Flag (0=Off, 1=On)
14 Window 1 Display Flag (0=Off, 1=On)
15 OBJ Window Display Flag (0=Off, 1=On)
The table summarizes the facilities of the separate BG modes (video modes).
Mode Rot/Scal Layers Size Tiles Colors Features
0 No 0123 256x256..512x515 1024 16/16..256/1 SFMABP
1 Mixed 012- (BG0,BG1 as above Mode 0, BG2 as below Mode 2)
2 Yes --23 128x128..1024x1024 256 256/1 S-MABP
3 Yes --2- 240x160 1 32768 --MABP
4 Yes --2- 240x160 2 256/1 --MABP
5 Yes --2- 160x128 2 32768 --MABP
Features: S)crolling, F)lip, M)osaic, A)lphaBlending, B)rightness, P)riority.
BG Modes 0-2 are Tile/Map-based. BG Modes 3-5 are Bitmap-based, in these modes 1 or 2 Frames (ie. bitmaps, or ‘full screen tiles’) exists, if two frames exist, either one can be displayed, and the other one can be redrawn in background.
Setting Forced Blank (Bit 7) causes the video controller to display white lines, and all VRAM, Palette RAM, and OAM may be accessed.
“When the internal HV synchronous counter cancels a forced blank during a display period, the display begins from the beginning, following the display of two vertical lines.” What ?
Setting H-Blank Interval Free (Bit 5) allows to access OAM during H-Blank time
By default, BG0-3 and OBJ Display Flags (Bit 8-12) are used to enable/disable BGs and OBJ. When enabling Window 0 and/or 1 (Bit 13-14), color special effects may be used, and BG0-3 and OBJ are controlled by the window(s).
In BG Modes 4 and 5 (Bitmap modes), either one of the two bitmaps/frames may be displayed (Bit 4), allowing the user to update the other (invisible) frame in background. In BG Mode 3, only one frame exists.
In BG Modes 0-2 (Tile/Map based modes), a similar effect may be gained by altering the base address(es) of BG Map and/or BG Character data.
Normally, red green blue intensities for a group of two pixels is output as BGRbgr (uppercase for left pixel at even xloc, lowercase for right pixel at odd xloc). When the Green Swap bit is set, each pixel group is output as BgRbGr (ie. green intensity of each two pixels exchanged).
Bit Expl.
0 Green Swap (0=Normal, 1=Swap)
1-15 Not used
This feature appears to be applied to the final picture (ie. after mixing the separate BG and OBJ layers). Eventually intended for other display types (with other pin-outs). With normal GBA hardware it is just producing an interesting dirt effect.
The NDS DISPCNT registers are 32bit (4000000h..4000003h), so Green Swap doesn’t exist in NDS mode, however, the NDS does support Green Swap in GBA mode.
Display status and Interrupt control. The H-Blank conditions are generated once per scanline, including for the ‘hidden’ scanlines during V-Blank.
Bit Expl.
0 V-Blank flag (Read only) (1=VBlank) (set in line 160..226; not 227)
1 H-Blank flag (Read only) (1=HBlank) (toggled in all lines, 0..227)
2 V-Counter flag (Read only) (1=Match) (set in selected line) (R)
3 V-Blank IRQ Enable (1=Enable) (R/W)
4 H-Blank IRQ Enable (1=Enable) (R/W)
5 V-Counter IRQ Enable (1=Enable) (R/W)
6 Not used (0) / DSi: LCD Initialization Ready (0=Busy, 1=Ready) (R)
7 Not used (0) / NDS: MSB of V-Vcount Setting (LYC.Bit8) (0..262)(R/W)
8-15 V-Count Setting (LYC) (0..227) (R/W)
The V-Count-Setting value is much the same as LYC of older gameboys, when its value is identical to the content of the VCOUNT register then the V-Counter flag is set (Bit 2), and (if enabled in Bit 5) an interrupt is requested.
Although the drawing time is only 960 cycles (240*4), the H-Blank flag is “0” for a total of 1006 cycles.
Indicates the currently drawn scanline, values in range from 160..227 indicate ‘hidden’ scanlines within VBlank area.
Bit Expl.
0-7 Current Scanline (LY) (0..227) (R)
8 Not used (0) / NDS: MSB of Current Scanline (LY.Bit8) (0..262) (R)
9-15 Not Used (0)
Note: This is much the same than the ‘LY’ register of older gameboys.
Bit Expl.
0-1 BG Priority (0-3, 0=Highest)
2-3 Character Base Block (0-3, in units of 16 KBytes) (=BG Tile Data)
4-5 Not used (must be zero) (except in NDS mode: MSBs of char base)
6 Mosaic (0=Disable, 1=Enable)
7 Colors/Palettes (0=16/16, 1=256/1)
8-12 Screen Base Block (0-31, in units of 2 KBytes) (=BG Map Data)
13 BG0/BG1: Not used (except in NDS mode: Ext Palette Slot for BG0/BG1)
13 BG2/BG3: Display Area Overflow (0=Transparent, 1=Wraparound)
14-15 Screen Size (0-3)
Internal Screen Size (dots) and size of BG Map (bytes):
Value Text Mode Rotation/Scaling Mode
0 256x256 (2K) 128x128 (256 bytes)
1 512x256 (4K) 256x256 (1K)
2 256x512 (4K) 512x512 (4K)
3 512x512 (8K) 1024x1024 (16K)
In case that some or all BGs are set to same priority then BG0 is having the highest, and BG3 the lowest priority.
In ‘Text Modes’, the screen size is organized as follows: The screen consists of one or more 256x256 pixel (32x32 tiles) areas. When Size=0: only 1 area (SC0), when Size=1 or Size=2: two areas (SC0,SC1 either horizontally or vertically arranged next to each other), when Size=3: four areas (SC0,SC1 in upper row, SC2,SC3 in lower row). Whereas SC0 is defined by the normal BG Map base address (Bit 8-12 of BGxCNT), SC1 uses same address +2K, SC2 address +4K, SC3 address +6K. When the screen is scrolled it’ll always wraparound.
In ‘Rotation/Scaling Modes’, the screen size is organized as follows, only one area (SC0) of variable size 128x128..1024x1024 pixels (16x16..128x128 tiles) exists. When the screen is rotated/scaled (or scrolled?) so that the LCD viewport reaches outside of the background/screen area, then BG may be either displayed as transparent or wraparound (Bit 13 of BGxCNT).
Bit Expl.
0-8 Offset (0-511)
9-15 Not used
Specifies the coordinate of the upperleft first visible dot of BG0 background layer, ie. used to scroll the BG0 area.
Same as above BG0HOFS and BG0VOFS for BG1 respectively.
Same as above BG0HOFS and BG0VOFS for BG2 respectively.
Same as above BG0HOFS and BG0VOFS for BG3 respectively.
The above BG scrolling registers are exclusively used in Text modes, ie. for all layers in BG Mode 0, and for the first two layers in BG mode 1.
In other BG modes (Rotation/Scaling and Bitmap modes) above registers are ignored. Instead, the screen may be scrolled by modifying the BG Rotation/Scaling Reference Point registers.
These registers are replacing the BG scrolling registers which are used for Text mode, ie. the X/Y coordinates specify the source position from inside of the BG Map/Bitmap of the pixel to be displayed at upper left of the GBA display. The normal BG scrolling registers are ignored in Rotation/Scaling and Bitmap modes.
Bit Expl.
0-7 Fractional portion (8 bits)
8-26 Integer portion (19 bits)
27 Sign (1 bit)
28-31 Not used
Because values are shifted left by eight, fractional portions may be specified in steps of 1/256 pixels (this would be relevant only if the screen is actually rotated or scaled). Normal signed 32bit values may be written to above registers (the most significant bits will be ignored and the value will be cut-down to 28bits, but this is no actual problem because signed values have set all MSBs to the same value).
The above reference points are automatically copied to internal registers during each vblank, specifying the origin for the first scanline. The internal registers are then incremented by dmx and dmy after each scanline.
Caution: Writing to a reference point register by software outside of the Vblank period does immediately copy the new value to the corresponding internal register, that means: in the current frame, the new value specifies the origin of the <current> scanline (instead of the topmost scanline).
Bit Expl.
0-7 Fractional portion (8 bits)
8-14 Integer portion (7 bits)
15 Sign (1 bit)
See below for details.
Same as above BG2 Reference Point, and Rotation/Scaling Parameters, for BG3 respectively.
When transforming a horizontal line, dx and dy specify the resulting gradient and magnification for that line. For example:
Horizontal line, length=100, dx=1, and dy=1. The resulting line would be drawn at 45 degrees, f(y)=1/1*x. Note that this would involve that line is magnified, the new length is SQR(100^2+100^2)=141.42. Yup, exactly - that’s the old a^2 + b^2 = c^2 formula.
These values define the resulting gradient and magnification for transformation of vertical lines. However, when rotating a square area (which is surrounded by horizontal and vertical lines), then the desired result should be usually a rotated <square> area (ie. not a parallelogram, for example).
Thus, dmx and dmy must be defined in direct relationship to dx and dy, taking the example above, we’d have to set dmx=-1, and dmy=1, f(x)=-1/1*y.
In result of rotation/scaling it may often happen that areas outside of the actual BG area become moved into the LCD viewport. Depending of the Area Overflow bit (BG2CNT and BG3CNT, Bit 13) these areas may be either displayed (by wrapping the BG area), or may be displayed transparent.
This works only in BG modes 1 and 2. The area overflow is ignored in Bitmap modes (BG modes 3-5), the outside of the Bitmaps is always transparent.
— more details and confusing or helpful formulas —
Rotation Center X and Y Coordinates (x0,y0)
Rotation Angle (alpha)
Magnification X and Y Values (xMag,yMag)
The display is rotated by ‘alpha’ degrees around the center.
The displayed picture is magnified by ‘xMag’ along x-Axis (Y=y0) and ‘yMag’ along y-Axis (X=x0).
A = Cos (alpha) / xMag ;distance moved in direction x, same line
B = Sin (alpha) / xMag ;distance moved in direction x, next line
C = Sin (alpha) / yMag ;distance moved in direction y, same line
D = Cos (alpha) / yMag ;distance moved in direction y, next line
Using the following expressions,
x0,y0 Rotation Center
x1,y1 Old Position of a pixel (before rotation/scaling)
x2,y2 New position of above pixel (after rotation scaling)
A,B,C,D BG2PA-BG2PD Parameters (as calculated above)
the following formula can be used to calculate x2,y2:
x2 = A(x1-x0) + B(y1-y0) + x0
y2 = C(x1-x0) + D(y1-y0) + y0
The Window Feature may be used to split the screen into four regions. The BG0-3,OBJ layers and Color Special Effects can be separately enabled or disabled in each of these regions.
DISPCNT Bits 13-15 are used to enable Window 0, Window 1, and/or OBJ Window regions, if any of these regions is enabled then the “Outside of Windows” region is automatically enabled, too.
DISPCNT Bits 8-12 are kept used as master enable bits for the BG0-3,OBJ layers, a layer is displayed only if both DISPCNT and WININ/OUT enable bits are set.
Bit Expl.
0-7 X2, Rightmost coordinate of window, plus 1
8-15 X1, Leftmost coordinate of window
Garbage values of X2>240 or X1>X2 are interpreted as X2=240.
Bit Expl.
0-7 Y2, Bottom-most coordinate of window, plus 1
8-15 Y1, Top-most coordinate of window
Garbage values of Y2>160 or Y1>Y2 are interpreted as Y2=160.
Bit Expl.
0-3 Window 0 BG0-BG3 Enable Bits (0=No Display, 1=Display)
4 Window 0 OBJ Enable Bit (0=No Display, 1=Display)
5 Window 0 Color Special Effect (0=Disable, 1=Enable)
6-7 Not used
8-11 Window 1 BG0-BG3 Enable Bits (0=No Display, 1=Display)
12 Window 1 OBJ Enable Bit (0=No Display, 1=Display)
13 Window 1 Color Special Effect (0=Disable, 1=Enable)
14-15 Not used
Bit Expl.
0-3 Outside BG0-BG3 Enable Bits (0=No Display, 1=Display)
4 Outside OBJ Enable Bit (0=No Display, 1=Display)
5 Outside Color Special Effect (0=Disable, 1=Enable)
6-7 Not used
8-11 OBJ Window BG0-BG3 Enable Bits (0=No Display, 1=Display)
12 OBJ Window OBJ Enable Bit (0=No Display, 1=Display)
13 OBJ Window Color Special Effect (0=Disable, 1=Enable)
14-15 Not used
The dimension of the OBJ Window is specified by OBJs which are having the “OBJ Mode” attribute being set to “OBJ Window”. Any non-transparent dots of any such OBJs are marked as OBJ Window area. The OBJ itself is not displayed.
The color, palette, and display priority of these OBJs are ignored. Both DISPCNT Bits 12 and 15 must be set when defining OBJ Window region(s).
In case that more than one window is enabled, and that these windows do overlap, Window 0 is having highest priority, Window 1 medium, and Obj Window lowest priority. Outside of Window is having zero priority, it is used for all dots which are not inside of any window region.
The Mosaic function can be separately enabled/disabled for BG0-BG3 by BG0CNT-BG3CNT Registers, as well as for each OBJ0-127 by OBJ attributes in OAM memory. Also, setting all of the bits below to zero effectively disables the mosaic function.
Bit Expl.
0-3 BG Mosaic H-Size (minus 1)
4-7 BG Mosaic V-Size (minus 1)
8-11 OBJ Mosaic H-Size (minus 1)
12-15 OBJ Mosaic V-Size (minus 1)
16-31 Not used
Example: When setting H-Size to 5, then pixels 0-5 of each display row are colorized as pixel 0, pixels 6-11 as pixel 6, pixels 12-17 as pixel 12, and so on.
Normally, a ‘mosaic-pixel’ is colorized by the color of the upperleft covered pixel. In many cases it might be more desireful to use the color of the pixel in the center of the covered area - this effect may be gained by scrolling the background (or by adjusting the OBJ position, as far as upper/left rows/columns of OBJ are transparent).
Two types of Special Effects are supported: Alpha Blending (Semi-Transparency) allows to combine colors of two selected surfaces. Brightness Increase/Decrease adjust the brightness of the selected surface.
Bit Expl.
0 BG0 1st Target Pixel (Background 0)
1 BG1 1st Target Pixel (Background 1)
2 BG2 1st Target Pixel (Background 2)
3 BG3 1st Target Pixel (Background 3)
4 OBJ 1st Target Pixel (Top-most OBJ pixel)
5 BD 1st Target Pixel (Backdrop)
6-7 Color Special Effect (0-3, see below)
0 = None (Special effects disabled)
1 = Alpha Blending (1st+2nd Target mixed)
2 = Brightness Increase (1st Target becomes whiter)
3 = Brightness Decrease (1st Target becomes blacker)
8 BG0 2nd Target Pixel (Background 0)
9 BG1 2nd Target Pixel (Background 1)
10 BG2 2nd Target Pixel (Background 2)
11 BG3 2nd Target Pixel (Background 3)
12 OBJ 2nd Target Pixel (Top-most OBJ pixel)
13 BD 2nd Target Pixel (Backdrop)
14-15 Not used
Selects the 1st Target layer(s) for special effects. For Alpha Blending/Semi-Transparency, it does also select the 2nd Target layer(s), which should have next lower display priority as the 1st Target.
However, any combinations are possible, including that all layers may be selected as both 1st+2nd target, in that case the top-most pixel will be used as 1st target, and the next lower pixel as 2nd target.
Used for Color Special Effects Mode 1, and for Semi-Transparent OBJs.
Bit Expl.
0-4 EVA Coefficient (1st Target) (0..16 = 0/16..16/16, 17..31=16/16)
5-7 Not used
8-12 EVB Coefficient (2nd Target) (0..16 = 0/16..16/16, 17..31=16/16)
13-15 Not used
For this effect, the top-most non-transparent pixel must be selected as 1st Target, and the next-lower non-transparent pixel must be selected as 2nd Target, if so - and only if so, then color intensities of 1st and 2nd Target are mixed together by using the parameters in BLDALPHA register, for each pixel each R, G, B intensities are calculated separately:
I = MIN ( 31, I1st*EVA + I2nd*EVB )
Otherwise - for example, if only one target exists, or if a non-transparent non-2nd-target pixel is moved between the two targets, or if 2nd target has higher display priority than 1st target - then only the top-most pixel is displayed (at normal intensity, regardless of BLDALPHA).
Used for Color Special Effects Modes 2 and 3.
Bit Expl.
0-4 EVY Coefficient (Brightness) (0..16 = 0/16..16/16, 17..31=16/16)
5-31 Not used
For each pixel each R, G, B intensities are calculated separately:
I = I1st + (31-I1st)*EVY ;For Brightness Increase
I = I1st - (I1st)*EVY ;For Brightness Decrease
The color intensities of any selected 1st target surface(s) are increased or decreased by using the parameter in BLDY register.
OBJs that are defined as ‘Semi-Transparent’ in OAM memory are always selected as 1st Target (regardless of BLDCNT Bit 4), and are always using Alpha Blending mode (regardless of BLDCNT Bit 6-7).
The BLDCNT register may be used to perform Brightness effects on the OBJ (and/or other BG/BD layers). However, if a semi-transparent OBJ pixel does overlap a 2nd target pixel, then semi-transparency becomes priority, and the brightness effect will not take place (neither on 1st, nor 2nd target).
Before special effects are applied, the display controller computes the OBJ priority ordering, and isolates the top-most OBJ pixel. In result, only the top-most OBJ pixel is recursed at the time when processing special effects. Ie. alpha blending and semi-transparency can be used for OBJ-to-BG or BG-to-OBJ , but not for OBJ-to-OBJ.
The GBA contains 96 Kbytes VRAM built-in, located at address 06000000-06017FFF, depending on the BG Mode used as follows:
06000000-0600FFFF 64 KBytes shared for BG Map and Tiles
06010000-06017FFF 32 KBytes OBJ Tiles
The shared 64K area can be split into BG Map area(s), and BG Tiles area(s), the respective addresses for Map and Tile areas are set up by BG0CNT-BG3CNT registers. The Map address may be specified in units of 2K (steps of 800h), the Tile address in units of 16K (steps of 4000h).
The tiles may have 4bit or 8bit color depth, minimum map size is 32x32 tiles, maximum is 64x64 tiles, up to 1024 tiles can be used per map.
Item Depth Required Memory
One Tile 4bit 20h bytes
One Tile 8bit 40h bytes
1024 Tiles 4bit 8000h (32K)
1024 Tiles 8bit 10000h (64K) - excluding some bytes for BG map
BG Map 32x32 800h (2K)
BG Map 64x64 2000h (8K)
The tiles may have 8bit color depth only, minimum map size is 16x16 tiles, maximum is 128x128 tiles, up to 256 tiles can be used per map.
Item Depth Required Memory
One Tile 8bit 40h bytes
256 Tiles 8bit 4000h (16K)
BG Map 16x16 100h bytes
BG Map 128x128 4000h (16K)
06000000-06013FFF 80 KBytes Frame 0 buffer (only 75K actually used)
06014000-06017FFF 16 KBytes OBJ Tiles
06000000-06009FFF 40 KBytes Frame 0 buffer (only 37.5K used in Mode 4)
0600A000-06013FFF 40 KBytes Frame 1 buffer (only 37.5K used in Mode 4)
06014000-06017FFF 16 KBytes OBJ Tiles
Additionally to the above VRAM, the GBA also contains 1 KByte Palette RAM (at 05000000h) and 1 KByte OAM (at 07000000h) which are both used by the display controller as well.
Each character (tile) consists of 8x8 dots (64 dots in total). The color depth may be either 4bit or 8bit (see BG0CNT-BG3CNT).
Each tile occupies 32 bytes of memory, the first 4 bytes for the topmost row of the tile, and so on. Each byte representing two dots, the lower 4 bits define the color for the left (!) dot, the upper 4 bits the color for the right dot.
Each tile occupies 64 bytes of memory, the first 8 bytes for the topmost row of the tile, and so on. Each byte selects the palette entry for each dot.
The display background consists of 8x8 dot tiles, the arrangement of these tiles is specified by the BG Screen Data (BG Map). The separate entries in this map are as follows:
Specifies the tile number and attributes. Note that BG tile numbers are always specified in steps of 1 (unlike OBJ tile numbers which are using steps of two in 256 color/1 palette mode).
Bit Expl.
0-9 Tile Number (0-1023) (a bit less in 256 color mode, because
there'd be otherwise no room for the bg map)
10 Horizontal Flip (0=Normal, 1=Mirrored)
11 Vertical Flip (0=Normal, 1=Mirrored)
12-15 Palette Number (0-15) (Not used in 256 color/1 palette mode)
A Text BG Map always consists of 32x32 entries (256x256 pixels), 400h entries = 800h bytes. However, depending on the BG Size, one, two, or four of these Maps may be used together, allowing to create backgrounds of 256x256, 512x256, 256x512, or 512x512 pixels, if so, the first map (SC0) is located at base+0, the next map (SC1) at base+800h, and so on.
In this mode, only 256 tiles can be used. There are no x/y-flip attributes, the color depth is always 256 colors/1 palette.
Bit Expl.
0-7 Tile Number (0-255)
The dimensions of Rotation/Scaling BG Maps depend on the BG size. For size 0-3 that are: 16x16 tiles (128x128 pixels), 32x32 tiles (256x256 pixels), 64x64 tiles (512x512 pixels), or 128x128 tiles (1024x1024 pixels).
The size and VRAM base address of the separate BG maps for BG0-3 are set up by BG0CNT-BG3CNT registers.
In BG Modes 3-5 the background is defined in form of a bitmap (unlike as for Tile/Map based BG modes). Bitmaps are implemented as BG2, with Rotation/Scaling support. As bitmap modes are occupying 80KBytes of BG memory, only 16KBytes of VRAM can be used for OBJ tiles.
Two bytes are associated to each pixel, directly defining one of the 32768 colors (without using palette data, and thus not supporting a ‘transparent’ BG color).
Bit Expl.
0-4 Red Intensity (0-31)
5-9 Green Intensity (0-31)
10-14 Blue Intensity (0-31)
15 Not used in GBA Mode (in NDS Mode: Alpha=0=Transparent, Alpha=1=Normal)
The first 480 bytes define the topmost line, the next 480 the next line, and so on. The background occupies 75 KBytes (06000000-06012BFF), most of the 80 Kbytes BG area, not allowing to redraw an invisible second frame in background, so this mode is mostly recommended for still images only.
One byte is associated to each pixel, selecting one of the 256 palette entries. Color 0 (backdrop) is transparent, and OBJs may be displayed behind the bitmap.
The first 240 bytes define the topmost line, the next 240 the next line, and so on. The background occupies 37.5 KBytes, allowing two frames to be used (06000000-060095FF for Frame 0, and 0600A000-060135FF for Frame 1).
Colors are defined as for Mode 3 (see above), but horizontal and vertical size are cut down to 160x128 pixels only - smaller than the physical dimensions of the LCD screen.
The background occupies exactly 40 KBytes, so that BG VRAM may be split into two frames (06000000-06009FFF for Frame 0, and 0600A000-06013FFF for Frame 1).
In BG modes 4,5, one Frame may be displayed (selected by DISPCNT Bit 4), the other Frame is invisible and may be redrawn in background.
Objects (OBJs) are moveable sprites. Up to 128 OBJs (of any size, up to 64x64 dots each) can be displayed per screen, and under best circumstances up to 128 OBJs (of small 8x8 dots size) can be displayed per horizontal display line.
The total available OBJ rendering cycles per line are
1210 (=304*4-6) If "H-Blank Interval Free" bit in DISPCNT register is 0
954 (=240*4-6) If "H-Blank Interval Free" bit in DISPCNT register is 1
The required rendering cycles are (depending on horizontal OBJ size)
Cycles per <n> Pixels OBJ Type OBJ Type Screen Pixel Range
n*1 cycles Normal OBJs 8..64 pixels
10+n*2 cycles Rotation/Scaling OBJs 8..64 pixels (area clipped)
10+n*2 cycles Rotation/Scaling OBJs 16..128 pixels (double size)
Caution:
The maximum number of OBJs per line is also affected by undisplayed (offscreen) OBJs which are having higher priority than displayed OBJs.
To avoid this, move displayed OBJs to the begin of OAM memory (ie. OBJ0 has highest priority, OBJ127 lowest).
Otherwise (in case that the program logic expects OBJs at fixed positions in OAM) at least take care to set the OBJ size of undisplayed OBJs to 8x8 with Rotation/Scaling disabled (this reduces the overload).
Does the above also apply for VERTICALLY OFFSCREEN (or VERTICALLY not on CURRENT LINE) sprites ?
OBJs are always combined of one or more 8x8 pixel Tiles (much like BG Tiles in BG Modes 0-2). However, OBJ Tiles are stored in a separate area in VRAM: 06010000-06017FFF (32 KBytes) in BG Mode 0-2, or 06014000-06017FFF (16 KBytes) in BG Mode 3-5.
Depending on the size of the above area (16K or 32K), and on the OBJ color depth (4bit or 8bit), 256-1024 8x8 dots OBJ Tiles can be defined.
This memory area contains Attributes which specify position, size, color depth, etc. appearance for each of the 128 OBJs. Additionally, it contains 32 OBJ Rotation/Scaling Parameter groups. OAM is located at 07000000-070003FF (sized 1 KByte).
There are 128 entries in OAM for each OBJ0-OBJ127. Each entry consists of 6 bytes (three 16bit Attributes). Attributes for OBJ0 are located at 07000000, for OBJ1 at 07000008, OBJ2 at 07000010, and so on.
As you can see, there are blank spaces at 07000006, 0700000E, 07000016, etc. - these 16bit values are used for OBJ Rotation/Scaling (as described in the next chapter) - they are not directly related to the separate OBJs.
Bit Expl.
0-7 Y-Coordinate (0-255)
8 Rotation/Scaling Flag (0=Off, 1=On)
When Rotation/Scaling used (Attribute 0, bit 8 set):
9 Double-Size Flag (0=Normal, 1=Double)
When Rotation/Scaling not used (Attribute 0, bit 8 cleared):
9 OBJ Disable (0=Normal, 1=Not displayed)
10-11 OBJ Mode (0=Normal, 1=Semi-Transparent, 2=OBJ Window, 3=Prohibited)
12 OBJ Mosaic (0=Off, 1=On)
13 Colors/Palettes (0=16/16, 1=256/1)
14-15 OBJ Shape (0=Square,1=Horizontal,2=Vertical,3=Prohibited)
Caution: A very large OBJ (of 128 pixels vertically, ie. a 64 pixels OBJ in a Double Size area) located at Y>128 will be treated as at Y>-128, the OBJ is then displayed parts offscreen at the TOP of the display, it is then NOT displayed at the bottom.
Bit Expl.
0-8 X-Coordinate (0-511)
When Rotation/Scaling used (Attribute 0, bit 8 set):
9-13 Rotation/Scaling Parameter Selection (0-31)
(Selects one of the 32 Rotation/Scaling Parameters that
can be defined in OAM, for details read next chapter.)
When Rotation/Scaling not used (Attribute 0, bit 8 cleared):
9-11 Not used
12 Horizontal Flip (0=Normal, 1=Mirrored)
13 Vertical Flip (0=Normal, 1=Mirrored)
14-15 OBJ Size (0..3, depends on OBJ Shape, see Attr 0)
Size Square Horizontal Vertical
0 8x8 16x8 8x16
1 16x16 32x8 8x32
2 32x32 32x16 16x32
3 64x64 64x32 32x64
Bit Expl.
0-9 Character Name (0-1023=Tile Number)
10-11 Priority relative to BG (0-3; 0=Highest)
12-15 Palette Number (0-15) (Not used in 256 color/1 palette mode)
The OBJ Mode may be Normal, Semi-Transparent, or OBJ Window.
Semi-Transparent means that the OBJ is used as ‘Alpha Blending 1st Target’ (regardless of BLDCNT register, for details see chapter about Color Special Effects).
OBJ Window means that the OBJ is not displayed, instead, dots with non-zero color are used as mask for the OBJ Window, see DISPCNT and WINOUT for details.
There are two situations which may divide the amount of available tiles by two (by four if both situations apply):
When using the 256 Colors/1 Palette mode, only each second tile may be used, the lower bit of the tile number should be zero (in 2-dimensional mapping mode, the bit is completely ignored).
When using BG Mode 3-5 (Bitmap Modes), only tile numbers 512-1023 may be used. That is because lower 16K of OBJ memory are used for BG. Attempts to use tiles 0-511 are ignored (not displayed).
In case that the ‘Priority relative to BG’ is the same than the priority of one of the background layers, then the OBJ becomes higher priority and is displayed on top of that BG layer.
Caution: Take care not to mess up BG Priority and OBJ priority. For example, the following would cause garbage to be displayed:
OBJ No. 0 with Priority relative to BG=1 ;hi OBJ prio, lo BG prio
OBJ No. 1 with Priority relative to BG=0 ;lo OBJ prio, hi BG prio
That is, OBJ0 is always having priority above OBJ1-127, so assigning a lower BG Priority to OBJ0 than for OBJ1-127 would be a bad idea.
As described in the previous chapter, there are blank spaces between each of the 128 OBJ Attribute Fields in OAM memory. These 128 16bit gaps are used to store OBJ Rotation/Scaling Parameters.
Four 16bit parameters (PA,PB,PC,PD) are required to define a complete group of Rotation/Scaling data. These are spread across OAM as such:
1st Group - PA=07000006, PB=0700000E, PC=07000016, PD=0700001E
2nd Group - PA=07000026, PB=0700002E, PC=07000036, PD=0700003E
etc.
By using all blank space (128 x 16bit), up to 32 of these groups (4 x 16bit each) can be defined in OAM.
Each OBJ that uses Rotation/Scaling may select between any of the above 32 parameter groups. For details, refer to the previous chapter about OBJ Attributes.
The meaning of the separate PA,PB,PC,PD values is identical as for BG, for details read the chapter about BG Rotation/Scaling.
The OBJ Reference Point is the upper left of the OBJ, ie. OBJ X/Y coordinates: X+0, Y+0.
The OBJ Rotation Center is always (or should be usually?) in the middle of the object, ie. for a 8x32 pixel OBJ, this would be at the OBJ X/Y coordinates: X+4, and Y+16.
When Double-Size is zero: The sprite is rotated, and then display inside of the normal-sized (not rotated) rectangular area - the edges of the rotated sprite will become invisible if they reach outside of that area.
When Double-Size is set: The sprite is rotated, and then display inside of the double-sized (not rotated) rectangular area - this ensures that the edges of the rotated sprite remain visible even if they would reach outside of the normal-sized area. (Except that, for example, rotating a 8x32 pixel sprite by 90 degrees would still cut off parts of the sprite as the double-size area isn’t large enough.)
Each OBJ tile consists of 8x8 dots, however, bigger OBJs can be displayed by combining several 8x8 tiles. The horizontal and vertical size for each OBJ may be separately defined in OAM, possible H/V sizes are 8,16,32,64 dots - allowing ‘square’ OBJs to be used (such like 8x8, 16x16, etc) as well as ‘rectangular’ OBJs (such like 8x32, 64x16, etc.)
When displaying an OBJ that contains of more than one 8x8 tile, one of the following two mapping modes can be used. In either case, the tile number of the upperleft tile must be specified in OAM memory.
This mapping mode assumes that the 1024 OBJ tiles are arranged as a matrix of 32x32 tiles / 256x256 pixels (In 256 color mode: 16x32 tiles / 128x256 pixels). Ie. the upper row of this matrix contains tiles 00h-1Fh, the next row tiles 20h-3Fh, and so on.
For example, when displaying a 16x16 pixel OBJ, with tile number set to 04h; The upper row of the OBJ will consist of tile 04h and 05h, the next row of 24h and 25h. (In 256 color mode: 04h and 06h, 24h and 26h.)
In this mode, tiles are mapped each after each other from 00h-3FFh.
Using the same example as above, the upper row of the OBJ will consist of tile 04h and 05h, the next row of tile 06h and 07h. (In 256 color mode: 04h and 06h, 08h and 0Ah.)
BG and OBJ palettes are using separate memory regions:
05000000-050001FF - BG Palette RAM (512 bytes, 256 colors)
05000200-050003FF - OBJ Palette RAM (512 bytes, 256 colors)
Each BG and OBJ palette RAM may be either split into 16 palettes with 16 colors each, or may be used as a single palette with 256 colors.
Note that some OBJs may access palette RAM in 16 color mode, while other OBJs may use 256 color mode at the same time. Same for BG0-BG3 layers.
Color 0 of all BG and OBJ palettes is transparent. Even though palettes are described as 16 (256) color palettes, only 15 (255) colors are actually visible.
Color 0 of BG Palette 0 is used as backdrop color. This color is displayed if an area of the screen is not covered by any non-transparent BG or OBJ dots.
Each color occupies two bytes (same as for 32768 color BG modes):
Bit Expl.
0-4 Red Intensity (0-31)
5-9 Green Intensity (0-31)
10-14 Blue Intensity (0-31)
15 Not used
Under normal circumstances (light source/viewing angle), the intensities 0-14 are practically all black, and only intensities 15-31 are resulting in visible medium..bright colors.
Note: The intensity problem appears in the 8bit CGB “compatibility” mode either. The original CGB display produced the opposite effect: Intensities 0-14 resulted in dark..medium colors, and intensities 15-31 resulted in bright colors. Any “medium” colors of CGB games will appear invisible/black on GBA hardware, and only very bright colors will be visible.
The drawing time for each dot is 4 CPU cycles.
Visible 240 dots, 57.221 us, 960 cycles - 78% of h-time
H-Blanking 68 dots, 16.212 us, 272 cycles - 22% of h-time
Total 308 dots, 73.433 us, 1232 cycles - ca. 13.620 kHz
VRAM and Palette RAM may be accessed during H-Blanking. OAM can accessed only if “H-Blank Interval Free” bit in DISPCNT register is set.
Visible (*) 160 lines, 11.749 ms, 197120 cycles - 70% of v-time
V-Blanking 68 lines, 4.994 ms, 83776 cycles - 30% of v-time
Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz
All VRAM, OAM, and Palette RAM may be accessed during V-Blanking.
Note that no H-Blank interrupts are generated within V-Blank period.
The system clock is 16.78MHz (16*1024*1024 Hz), one cycle is thus approx. 59.59ns.
(*) Even though vertical screen size is 160 lines, the upper 8 lines are not <really> visible, these lines are covered by a shadow when holding the GBA orientated towards a light source, the lines are effectively black - and should not be used to display important information.
The LCD display is using some sort of interlace in which even scanlines are dimmed in each second frame, and odd scanlines are dimmed in each other frame (it does always render ALL lines in ALL frames, but half of them are dimmed).
The effect can be seen when displaying some horizontal lines in each second frame, and hiding them in each other frame: the hardware will randomly show the lines in dimmed or non-dimmed form (depending on whether the test was started in an even or odd frame).
Unknown if it’s possible to determine the even/off frame state by software (or possibly to reset the hardware to this or that state by software).
Note: The NDS is applying some sort of frameskip to GBA games, about every 3 seconds there will by a missing (or maybe: inserted) frame, ie. a GBA game that is updating the display in sync with GBA interlace will get offsync on NDS consoles.
The GBA supplies four ‘analogue’ sound channels for Tone and Noise (mostly compatible to CGB sound), as well as two ‘digital’ sound channels (which can be used to replay 8bit DMA sample data).
The GBA includes only a single (mono) speaker built-in, each channel may be output to either left and/or right channels by using the external line-out connector (for stereo headphones, etc).
Bit Expl.
0-2 R/W Number of sweep shift (n=0-7)
3 R/W Sweep Frequency Direction (0=Increase, 1=Decrease)
4-6 R/W Sweep Time; units of 7.8ms (0-7, min=7.8ms, max=54.7ms)
7-15 - Not used
Sweep is disabled by setting Sweep Time to zero, if so, the direction bit should be set.
The change of frequency (NR13,NR14) at each shift is calculated by the following formula where X(0) is initial freq & X(t-1) is last freq:
X(t) = X(t-1) +/- X(t-1)/2^n
Bit Expl.
0-5 W Sound length; units of (64-n)/256s (0-63)
6-7 R/W Wave Pattern Duty (0-3, see below)
8-10 R/W Envelope Step-Time; units of n/64s (1-7, 0=No Envelope)
11 R/W Envelope Direction (0=Decrease, 1=Increase)
12-15 R/W Initial Volume of envelope (1-15, 0=No Sound)
Wave Duty:
0: 12.5% ( -_______-_______-_______ )
1: 25% ( --______--______--______ )
2: 50% ( ----____----____----____ ) (normal)
3: 75% ( ------__------__------__ )
The Length value is used only if Bit 6 in NR14 is set.
Bit Expl.
0-10 W Frequency; 131072/(2048-n)Hz (0-2047)
11-13 - Not used
14 R/W Length Flag (1=Stop output when length in NR11 expires)
15 W Initial (1=Restart Sound)
16-31 - Not used
This sound channel works exactly as channel 1, except that it doesn’t have a Tone Envelope/Sweep Register.
For details, refer to channel 1 description.
This channel can be used to output digital sound, the length of the sample buffer (Wave RAM) can be either 32 or 64 digits (4bit samples). This sound channel can be also used to output normal tones when initializing the Wave RAM by a square wave. This channel doesn’t have a volume envelope register.
Bit Expl.
0-4 - Not used
5 R/W Wave RAM Dimension (0=One bank/32 digits, 1=Two banks/64 digits)
6 R/W Wave RAM Bank Number (0-1, see below)
7 R/W Sound Channel 3 Off (0=Stop, 1=Playback)
8-15 - Not used
The currently selected Bank Number (Bit 6) will be played back, while reading/writing to/from wave RAM will address the other (not selected) bank. When dimension is set to two banks, output will start by replaying the currently selected bank.
Bit Expl.
0-7 W Sound length; units of (256-n)/256s (0-255)
8-12 - Not used.
13-14 R/W Sound Volume (0=Mute/Zero, 1=100%, 2=50%, 3=25%)
15 R/W Force Volume (0=Use above, 1=Force 75% regardless of above)
The Length value is used only if Bit 6 in NR34 is set.
Bit Expl.
0-10 W Sample Rate; 2097152/(2048-n) Hz (0-2047)
11-13 - Not used
14 R/W Length Flag (1=Stop output when length in NR31 expires)
15 W Initial (1=Restart Sound)
16-31 - Not used
The above sample rate specifies the number of wave RAM digits per second, the actual tone frequency depends on the wave RAM content, for example:
Wave RAM, single bank 32 digits Tone Frequency
FFFFFFFFFFFFFFFF0000000000000000 65536/(2048-n) Hz
FFFFFFFF00000000FFFFFFFF00000000 131072/(2048-n) Hz
FFFF0000FFFF0000FFFF0000FFFF0000 262144/(2048-n) Hz
FF00FF00FF00FF00FF00FF00FF00FF00 524288/(2048-n) Hz
F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0 1048576/(2048-n) Hz
This area contains 16 bytes (32 x 4bits) Wave Pattern data which is output by channel 3. Data is played back ordered as follows: MSBs of 1st byte, followed by LSBs of 1st byte, followed by MSBs of 2nd byte, and so on - this results in a confusing ordering when filling Wave RAM in units of 16bit data - ie. samples would be then located in Bits 4-7, 0-3, 12-15, 8-11.
In the GBA, two Wave Patterns exists (each 32 x 4bits), either one may be played (as selected in NR30 register), the other bank may be accessed by the users. After all 32 samples have been played, output of the same bank (or other bank, as specified in NR30) will be automatically restarted.
Internally, Wave RAM is a giant shift-register, there is no pointer which is addressing the currently played digit. Instead, the entire 128 bits are shifted, and the 4 least significant bits are output.
Thus, when reading from Wave RAM, data might have changed its position. And, when writing to Wave RAM all data should be updated (it’d be no good idea to assume that old data is still located at the same position where it has been written to previously).
This channel is used to output white noise. This is done by randomly switching the amplitude between high and low at a given frequency. Depending on the frequency the noise will appear ‘harder’ or ‘softer’.
It is also possible to influence the function of the random generator, so the that the output becomes more regular, resulting in a limited ability to output Tone instead of Noise.
Bit Expl.
0-5 W Sound length; units of (64-n)/256s (0-63)
6-7 - Not used
8-10 R/W Envelope Step-Time; units of n/64s (1-7, 0=No Envelope)
11 R/W Envelope Direction (0=Decrease, 1=Increase)
12-15 R/W Initial Volume of envelope (1-15, 0=No Sound)
16-31 - Not used
The Length value is used only if Bit 6 in NR44 is set.
The amplitude is randomly switched between high and low at the given frequency. A higher frequency will make the noise to appear ‘softer’.
When Bit 3 is set, the output will become more regular, and some frequencies will sound more like Tone than Noise.
Bit Expl.
0-2 R/W Dividing Ratio of Frequencies (r)
3 R/W Counter Step/Width (0=15 bits, 1=7 bits)
4-7 R/W Shift Clock Frequency (s)
8-13 - Not used
14 R/W Length Flag (1=Stop output when length in NR41 expires)
15 W Initial (1=Restart Sound)
16-31 - Not used
Frequency = 524288 Hz / r / 2^(s+1) ;For r=0 assume r=0.5 instead
Noise randomly switches between HIGH and LOW levels, the output levels are calculated by a shift register (X), at the selected frequency, as such:
7bit: X=X SHR 1, IF carry THEN Out=HIGH, X=X XOR 60h ELSE Out=LOW
15bit: X=X SHR 1, IF carry THEN Out=HIGH, X=X XOR 6000h ELSE Out=LOW
The initial value when (re-)starting the sound is X=40h (7bit) or X=4000h (15bit). The data stream repeats after 7Fh (7bit) or 7FFFh (15bit) steps.
The GBA contains two DMA sound channels (A and B), each allowing to replay digital sound (signed 8bit data, ie. -128..+127). Data can be transferred from INTERNAL memory (not sure if EXTERNAL memory works also ?) to FIFO by using DMA channel 1 or 2, the sample rate is generated by using one of the Timers.
These two registers may receive 32bit (4 bytes) of audio data (Data 0-3, Data 0 being located in least significant byte which is replayed first).
Internally, the capacity of the FIFO is 8 x 32bit (32 bytes), allowing to buffer a small amount of samples. As the name says (First In First Out), oldest data is replayed first.
Same as above, for Sound B.
Select Timer 0 or 1 in SOUNDCNT_H control register.
Clear the FIFO.
Manually write a sample byte to the FIFO.
Initialize transfer mode for DMA 1 or 2.
Initialize DMA Sound settings in sound control register.
Start the timer.
The pseudo-procedure below is automatically repeated.
If Timer overflows then
Move 8bit data from FIFO to sound circuit.
If FIFO contains only 4 x 32bits (16 bytes) then
Request more data per DMA
Receive 4 x 32bit (16 bytes) per DMA
Endif
Endif
This playback mechanism will be repeated forever, regardless of the actual length of the sample buffer.
The buffer-end may be determined by counting sound Timer IRQs (each sample byte), or sound DMA IRQs (each 16th sample byte). Both methods would require a lot of CPU time (IRQ processing), and both would fail if interrupts are disabled for a longer period.
Better solutions would be to synchronize the sample rate/buffer length with V-blanks, or to use a second timer (in count up/slave mode) which produces an IRQ after the desired number of samples.
The GBA hardware does internally re-sample all sound output to 32.768kHz (default SOUNDBIAS setting). It’d thus do not make much sense to use higher DMA/Timer rates. Best re-sampling accuracy can be gained by using DMA/Timer rates of 32.768kHz, 16.384kHz, or 8.192kHz (ie. fragments of the physical output rate).
Bit Expl.
0-2 R/W Sound 1-4 Master Volume RIGHT (0-7)
3 - Not used
4-6 R/W Sound 1-4 Master Volume LEFT (0-7)
7 - Not used
8-11 R/W Sound 1-4 Enable Flags RIGHT (each Bit 8-11, 0=Disable, 1=Enable)
12-15 R/W Sound 1-4 Enable Flags LEFT (each Bit 12-15, 0=Disable, 1=Enable)
Bit Expl.
0-1 R/W Sound # 1-4 Volume (0=25%, 1=50%, 2=100%, 3=Prohibited)
2 R/W DMA Sound A Volume (0=50%, 1=100%)
3 R/W DMA Sound B Volume (0=50%, 1=100%)
4-7 - Not used
8 R/W DMA Sound A Enable RIGHT (0=Disable, 1=Enable)
9 R/W DMA Sound A Enable LEFT (0=Disable, 1=Enable)
10 R/W DMA Sound A Timer Select (0=Timer 0, 1=Timer 1)
11 W? DMA Sound A Reset FIFO (1=Reset)
12 R/W DMA Sound B Enable RIGHT (0=Disable, 1=Enable)
13 R/W DMA Sound B Enable LEFT (0=Disable, 1=Enable)
14 R/W DMA Sound B Timer Select (0=Timer 0, 1=Timer 1)
15 W? DMA Sound B Reset FIFO (1=Reset)
Bits 0-3 are automatically set when starting sound output, and are automatically cleared when a sound ends. (Ie. when the length expires, as far as length is enabled. The bits are NOT reset when an volume envelope ends.)
Bit Expl.
0 R Sound 1 ON flag (Read Only)
1 R Sound 2 ON flag (Read Only)
2 R Sound 3 ON flag (Read Only)
3 R Sound 4 ON flag (Read Only)
4-6 - Not used
7 R/W PSG/FIFO Master Enable (0=Disable, 1=Enable) (Read/Write)
8-31 - Not used
While Bit 7 is cleared, both PSG and FIFO sounds are disabled, and all PSG registers at 4000060h..4000081h are reset to zero (and must be re-initialized after re-enabling sound). However, registers 4000082h and 4000088h are kept read/write-able (of which, 4000082h has no function when sound is off, whilst 4000088h does work even when sound is off).
This register controls the final sound output. The default setting is 0200h, it is normally not required to change this value.
Bit Expl.
0 - Not used
1-9 R/W Bias Level (Default=100h, converting signed samples into unsigned)
10-13 - Not used
14-15 R/W Amplitude Resolution/Sampling Cycle (Default=0, see below)
16-31 - Not used
Amplitude Resolution/Sampling Cycle (0-3):
0 9bit / 32.768kHz (Default, best for DMA channels A,B)
1 8bit / 65.536kHz
2 7bit / 131.072kHz
3 6bit / 262.144kHz (Best for PSG channels 1-4)
For more information on this register, read the descriptions below.
Each of the two FIFOs can span the FULL output range (+/-200h).
Each of the four PSGs can span one QUARTER of the output range (+/-80h).
The current output levels of all six channels are added together by hardware.
So together, the FIFOs and PSGs, could reach THRICE the range (+/-600h).
The BIAS value is added to that signed value. With default BIAS (200h), the possible range becomes -400h..+800h, however, values that exceed the unsigned 10bit output range of 0..3FFh are clipped to MinMax(0,3FFh).
The PSG channels 1-4 are internally generated at 262.144kHz, and DMA sound A-B could be theoretically generated at timer rates up to 16.78MHz. However, the final sound output is resampled to a rate of 32.768kHz, at 9bit depth (the above 10bit value, divided by two). If necessary, rates higher than 32.768kHz can be selected in the SOUNDBIAS register, that would result in a depth smaller than 9bit though.
Okay, now comes the actual output. The GBA can output only two voltages (low and high), these ‘bits’ are output at system clock speed (16.78MHz). If using the default 32.768kHz sampling rate, then 512 bits are output per sample (512*32K=16M). Each sample value (9bit range, N=0..511), would be then output as N low bits, followed by 512-N high bits. The resulting ‘noise’ is smoothed down by capacitors, by the speaker, and by human hearing, so that it will effectively sound like clean D/A converted 9bit voltages at 32kHz sampling rate.
Normally use 200h for clean sound output. A value of 000h might make sense during periods when no sound is output (causing the PWM circuit to output low-bits only, which is eventually reducing the power consumption, and/or preventing 32KHz noise). Note: Using the SoundBias function (SWI 19h) allows to change the level by slowly incrementing or decrementing it (without hard scratch noise).
When not using sound output, power consumption can be reduced by setting both 4000084h (PSG/FIFO) and 4000088h (BIAS) to zero.
The GBA sound controller is mostly the same than that of older monochrome gameboy and CGB. The following changes have been done:
Two new sound channels have been added that may be used to replay 8bit digital sound. Sample rate and sample data must be supplied by using a Timer and a DMA channel.
The SOUNDCNT_H register controls the new DMA channels - as well as mixing with the four old channels. The SOUNDBIAS register controls the final sound output.
The length of the Wave RAM is doubled by dividing it into two banks of 32 digits each, either one or both banks may be replayed (one after each other), for details check NR30 Bit 5-6. Optionally, the sound may be output at 75% volume, for details check NR32 Bit 7.
NR50 is not supporting Vin signals (that’s been an external sound input from cartridge).
The GBAs sound register are located at 04000060-040000AE instead of at FF10-FF3F as in CGB and monochrome gameboy. However, note that there have been new blank spaces inserted between some of the separate registers - therefore it is NOT possible to port CGB software to GBA just by changing the sound base address.
In some cases two of the old 8bit registers are packed into a 16bit register and may be accessed as such.
The GBA includes four incrementing 16bit timers.
Timer 0 and 1 can be used to supply the sample rate for DMA sound channel A and/or B.
Writing to these registers initializes the <reload> value (but does not directly affect the current counter value). Reading returns the current <counter> value (or the recent/frozen counter value if the timer has been stopped).
The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows, or when the timer start bit becomes changed from 0 to 1.
Note: When simultaneously changing the start bit from 0 to 1, and setting the reload value at the same time (by a single 32bit I/O operation), then the newly written reload value is recognized as new counter value.
Bit Expl.
0-1 Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)
2 Count-up Timing (0=Normal, 1=See below) ;Not used in TM0CNT_H
3-5 Not used
6 Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow)
7 Timer Start/Stop (0=Stop, 1=Operate)
8-15 Not used
When Count-up Timing is enabled, the prescaler value is ignored, instead the time is incremented each time when the previous counter overflows. This function cannot be used for Timer 0 (as it is the first timer).
F = System Clock (16.78MHz).
The GBA includes four DMA channels, the highest priority is assigned to DMA0, followed by DMA1, DMA2, and DMA3. DMA Channels with lower priority are paused until channels with higher priority have completed.
The CPU is paused when DMA transfers are active, however, the CPU is operating during the periods when Sound/Blanking DMA transfers are paused.
DMA0 - highest priority, best for timing critical transfers (eg. HBlank DMA).
DMA1 and DMA2 - can be used to feed digital sample data to the Sound FIFOs.
DMA3 - can be used to write to Game Pak ROM/FlashROM (but not GamePak SRAM).
Beside for that, each DMA 0-3 may be used for whatever general purposes.
The most significant address bits are ignored, only the least significant 27 or 28 bits are used (max 07FFFFFFh internal memory, or max 0FFFFFFFh any memory - except SRAM ?!).
The most significant address bits are ignored, only the least significant 27 or 28 bits are used (max. 07FFFFFFh internal memory or 0FFFFFFFh any memory - except SRAM ?!).
Specifies the number of data units to be transferred, each unit is 16bit or 32bit depending on the transfer type, a value of zero is treated as max length (ie. 4000h, or 10000h for DMA3).
Bit Expl.
0-4 Not used
5-6 Dest Addr Control (0=Increment,1=Decrement,2=Fixed,3=Increment/Reload)
7-8 Source Adr Control (0=Increment,1=Decrement,2=Fixed,3=Prohibited)
9 DMA Repeat (0=Off, 1=On) (Must be zero if Bit 11 set)
10 DMA Transfer Type (0=16bit, 1=32bit)
11 Game Pak DRQ - DMA3 only - (0=Normal, 1=DRQ <from> Game Pak, DMA3)
12-13 DMA Start Timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Special)
The 'Special' setting (Start Timing=3) depends on the DMA channel:
DMA0=Prohibited, DMA1/DMA2=Sound FIFO, DMA3=Video Capture
14 IRQ upon end of Word Count (0=Disable, 1=Enable)
15 DMA Enable (0=Off, 1=On)
After changing the Enable bit from 0 to 1, wait 2 clock cycles before accessing any DMA related registers.
When accessing OAM (7000000h) or OBJ VRAM (6010000h) by HBlank Timing, then the “H-Blank Interval Free” bit in DISPCNT register must be set.
The SAD, DAD, and CNT_L registers are holding the initial start addresses, and initial length. The hardware does NOT change the content of these registers during or after the transfer.
The actual transfer takes place by using internal pointer/counter registers. The initial values are copied into internal regs under the following circumstances:
Upon DMA Enable (Bit 15) changing from 0 to 1: Reloads SAD, DAD, CNT_L.
Upon Repeat: Reloads CNT_L, and optionally DAD (Increment+Reload).
If the Repeat bit is cleared: The Enable bit is automatically cleared after the specified number of data units has been transferred.
If the Repeat bit is set: The Enable bit remains set after the transfer, and the transfer will be restarted each time when the Start condition (eg. HBlank, Fifo) becomes true. The specified number of data units is transferred <each> time when the transfer is (re-)started. The transfer will be repeated forever, until it gets stopped by software.
In this mode, the DMA Repeat bit must be set, and the destination address must be FIFO_A (040000A0h) or FIFO_B (040000A4h).
Upon DMA request from sound controller, 4 units of 32bits (16 bytes) are transferred (both Word Count register and DMA Transfer Type bit are ignored). The destination address will not be incremented in FIFO mode.
Keep in mind that DMA channels of higher priority may offhold sound DMA. For example, when using a 64 kHz sample rate, 16 bytes of sound DMA data are requested each 0.25ms (4 kHz), at this time another 16 bytes are still in the FIFO so that there’s still 0.25ms time to satisfy the DMA request. Thus DMAs with higher priority should not be operated for longer than 0.25ms. (This problem does not arise for HBlank transfers as HBlank time is limited to 16.212us.)
Only DMA 3 may be used to transfer data to/from Game Pak ROM or Flash ROM - it cannot access Game Pak SRAM though (as SRAM data bus is limited to 8bit units). In normal mode, DMA is requested as long until Word Count becomes zero. When setting the ‘Game Pack DRQ’ bit, then the cartridge must contain an external circuit which outputs a /DREQ signal. Note that there is only one pin for /DREQ and /IREQ, thus the cartridge may not supply /IREQs while using DRQ mode.
Intended to copy a bitmap from memory (or from external hardware/camera) to VRAM. When using this transfer mode, set the repeat bit, and write the number of data units (per scanline) to the word count register. Capture works similar like HBlank DMA, however, the transfer is started when VCOUNT=2, it is then repeated each scanline, and it gets stopped when VCOUNT=162.
The DMA Enable flag (Bit 15) is automatically cleared upon completion of the transfer. The user may also clear this bit manually in order to stop the transfer (obviously this is possible for Sound/Blanking DMAs only, in all other cases the CPU is stopped until the transfer completes by itself).
Except for the first data unit, all units are transferred by sequential reads and writes. For n data units, the DMA transfer time is:
2N+2(n-1)S+xI
Of which, 1N+(n-1)S are read cycles, and the other 1N+(n-1)S are write cycles, actual number of cycles depends on the waitstates and bus-width of the source and destination areas (as described in CPU Instruction Cycle Times chapter). Internal time for DMA processing is 2I (normally), or 4I (if both source and destination are in gamepak memory area).
DMA lockup when stopping while starting ???
Capture delayed, Capture Enable=AutoCleared ???
The GBAs Serial Port may be used in various different communication modes. Normal mode may exchange data between two GBAs (or to transfer data from master GBA to several slave GBAs in one-way direction).
Multi-player mode may exchange data between up to four GBAs. UART mode works much like a RS232 interface. JOY Bus mode uses a standardized Nintendo protocol. And General Purpose mode allows to mis-use the ‘serial’ port as bi-directional 4bit parallel port.
Note: The Nintendo DS does not include a Serial Port.
Even though early GBA prototypes have been intended to support IR communication, this feature has been removed.
However, Nintendo is apparently considering to provide an external IR adapter (to be connected to the SIO connector, being accessed in General Purpose mode).
Also, it’d be theoretically possible to include IR ports built-in in game cartridges (as done for some older 8bit/monochrome Hudson games).
This mode is used to communicate between two units.
Transfer rates of 256Kbit/s or 2Mbit/s can be selected, however, the fast 2Mbit/s is intended ONLY for special hardware expansions that are DIRECTLY connected to the GBA link port (ie. without a cable being located between the GBA and expansion hardware). In normal cases, always use 256Kbit/s transfer rate which provides stable results.
Transfer lengths of 8bit or 32bit may be used, the 8bit mode is the same as for older DMG/CGB gameboys, however, the voltages for “GBA cartridges in GBAs” are different as for “DMG/CGB cartridges in DMG/CGB/GBAs”, ie. it is not possible to communicate between DMG/CGB games and GBA games.
Bit Expl.
0-3 Undocumented (current SC,SD,SI,SO state, as for General Purpose mode)
4-8 Not used (Should be 0, bits are read/write-able though)
9-13 Not used (Always 0, read only)
14 Not used (Should be 0, bit is read/write-able though)
15 Must be zero (0) for Normal/Multiplayer/UART modes
Bit Expl.
0 Shift Clock (SC) (0=External, 1=Internal)
1 Internal Shift Clock (0=256KHz, 1=2MHz)
2 SI State (opponents SO) (0=Low, 1=High/None) --- (Read Only)
3 SO during inactivity (0=Low, 1=High) (applied ONLY when Bit7=0)
4-6 Not used (Read only, always 0 ?)
7 Start Bit (0=Inactive/Ready, 1=Start/Active)
8-11 Not used (R/W, should be 0)
12 Transfer Length (0=8bit, 1=32bit)
13 Must be "0" for Normal Mode
14 IRQ Enable (0=Disable, 1=Want IRQ upon completion)
15 Not used (Read only, always 0)
The Start bit is automatically reset when the transfer completes, ie. when all 8 or 32 bits are transferred, at that time an IRQ may be generated.
For 8bit normal mode. Contains 8bit data (only lower 8bit are used). Outgoing data should be written to this register before starting the transfer. During transfer, transmitted bits are shifted-out (MSB first), and received bits are shifted-in simultaneously. Upon transfer completion, the register contains the received 8bit value.
Same as above SIODATA8, for 32bit normal transfer mode respectively.
SIOCNT/RCNT must be set to 32bit normal mode <before> writing to SIODATA32.
First, initialize RCNT register. Second, set mode/clock bits in SIOCNT with startbit cleared. For master: select internal clock, and (in most cases) specify 256KHz as transfer rate. For slave: select external clock, the local transfer rate selection is then ignored, as the transfer rate is supplied by the remote GBA (or other computer, which might supply custom transfer rates).
Third, set the startbit in SIOCNT with mode/clock bits unchanged.
Initialize data which is to be sent to master.
Set Start flag.
Set SO to LOW to indicate that master may start now.
Wait for IRQ (or for Start bit to become zero). (Check timeout here!)
Set SO to HIGH to indicate that we are not ready.
Process received data.
Repeat procedure if more data is to be transferred.
(or is so=high done automatically? would be fine - more stable - otherwise master may still need delay)
Initialize data which is to be sent to master.
Set Start=0 and SO=0 (SO=LOW indicates that slave is (almost) ready).
Set Start=1 and SO=1 (SO=HIGH indicates not ready, applied after transfer).
(Expl. Old SO=LOW kept output until 1st clock bit received).
(Expl. New SO=HIGH is automatically output at transfer completion).
Set SO to LOW to indicate that master may start now.
Wait for IRQ (or for Start bit to become zero). (Check timeout here!)
Process received data.
Repeat procedure if more data is to be transferred.
Initialize data which is to be sent to slave.
Wait for SI to become LOW (slave ready). (Check timeout here!)
Set Start flag.
Wait for IRQ (or for Start bit to become zero).
Process received data.
Repeat procedure if more data is to be transferred.
During inactive transfer, the shift clock (SC) is high. The transmit (SO) and receive (SI) data lines may be manually controlled as described above.
When master sends SC=LOW, each master and slave must output the next outgoing data bit to SO. When master sends SC=HIGH, each master and slave must read out the opponents data bit from SI. This is repeated for each of the 8 or 32 bits, and when completed SC will be kept high again.
Either 256KHz or 2MHz rates can be selected for SC, so max 32KBytes (256Kbit) or 128KBytes (2Mbit) can be transferred per second. However, the software must process each 8bit or 32bit of transmitted data separately, so the actual transfer rate will be reduced by the time spent on handling each data unit.
Only 256KHz provides stable results in most cases (such like when linking between two GBAs). The 2MHz rate is intended for special expansion hardware (with very short wires) only.
When using normal mode with multiplay-cables, data isn’t exchanged between first and second GBA as usually. Instead, data is shifted from first to last GBA (the first GBA receives zero, because master SI is shortcut to GND).
This behaviour may be used for fast ONE-WAY data transfer from master to all other GBAs. For example (3 GBAs linked):
Step Sender 1st Recipient 2nd Recipient
Transfer 1: DATA #0 --> UNDEF --> UNDEF -->
Transfer 2: DATA #1 --> DATA #0 --> UNDEF -->
Transfer 3: DATA #2 --> DATA #1 --> DATA #0 -->
Transfer 4: DATA #3 --> DATA #2 --> DATA #1 -->
The recipients should not output any own data, instead they should forward the previously received data to the next recipient during next transfer (just keep the incoming data unmodified in the data register).
Due to the delayed forwarding, 2nd recipient should ignore the first incoming data. After the last transfer, the sender must send one (or more) dummy data unit(s), so that the last data is forwarded to the 2nd (or further) recipient(s).
Multi-Player mode can be used to communicate between up to 4 units.
Bit Expl.
0-3 Undocumented (current SC,SD,SI,SO state, as for General Purpose mode)
4-8 Not used (Should be 0, bits are read/write-able though)
9-13 Not used (Always 0, read only)
14 Not used (Should be 0, bit is read/write-able though)
15 Must be zero (0) for Normal/Multiplayer/UART modes
Note: Even though undocumented, many Nintendo games are using Bit 0 to test current SC state in multiplay mode.
Bit Expl.
0-1 Baud Rate (0-3: 9600,38400,57600,115200 bps)
2 SI-Terminal (0=Parent, 1=Child) (Read Only)
3 SD-Terminal (0=Bad connection, 1=All GBAs Ready) (Read Only)
4-5 Multi-Player ID (0=Parent, 1-3=1st-3rd child) (Read Only)
6 Multi-Player Error (0=Normal, 1=Error) (Read Only)
7 Start/Busy Bit (0=Inactive, 1=Start/Busy) (Read Only for Slaves)
8-11 Not used (R/W, should be 0)
12 Must be "0" for Multi-Player mode
13 Must be "1" for Multi-Player mode
14 IRQ Enable (0=Disable, 1=Want IRQ upon completion)
15 Not used (Read only, always 0)
The ID Bits are undefined until the first transfer has completed.
Outgoing data (16 bit) which is to be sent to the other GBAs.
These registers are automatically reset to FFFFh upon transfer start.
After transfer, these registers contain incoming data (16bit each) from all remote GBAs (if any / otherwise still FFFFh), as well as the local outgoing SIOMLT_SEND data.
Ie. after the transfer, all connected GBAs will contain the same values in their SIOMULTI0-3 registers.
Initialize RCNT Bit 14-15 and SIOCNT Bit 12-13 to select Multi-Player mode.
Read SIOCNT Bit 3 to verify that all GBAs are in Multi-Player mode.
Read SIOCNT Bit 2 to detect whether this is the Parent/Master unit.
Write outgoing data to SIODATA_SEND.
Master must set Start bit.
All units must process received data in SIOMULTI0-3 when transfer completed.
After the first successful transfer, ID Bits in SIOCNT are valid.
If more data is to be transferred, repeat procedure.
The parent unit blindly sends data regardless of whether childs have already processed old data/supplied new data. So, parent unit might be required to insert delays between each transfer, and/or perform error checking.
Also, slave units may signalize that they are not ready by temporarily switching into another communication mode (which does not output SD High, as Multi-Player mode does during inactivity).
Beginning
The masters SI pin is always LOW.
When all GBAs are in Multiplayer mode (ready) SD is HIGH.
When master starts the transfer, it sets SC=LOW, slaves receive Busy bit.
Step A
ID Bits in master unit are set to 0.
Master outputs Startbit (LOW), 16bit Data, Stopbit (HIGH) through SD.
This data is written to SIOMULTI0 of all GBAs (including master).
Master forwards LOW from its SO to 1st childs SI.
Transfer ends if next child does not output data after certain time.
Step B
ID Bits in 1st child unit are set to 1.
1st Child outputs Startbit (LOW), 16bit Data, Stopbit (HIGH) through SD.
This data is written to SIOMULTI1 of all GBAs (including 1st child).
1st child forwards LOW from its SO to 2nd childs SI.
Transfer ends if next child does not output data after certain time.
Step C
ID Bits in 2nd child unit are set to 2.
2nd Child outputs Startbit (LOW), 16bit Data, Stopbit (HIGH) through SD.
This data is written to SIOMULTI2 of all GBAs (including 2nd child).
2nd child forwards LOW from its SO to 3rd childs SI.
Transfer ends if next child does not output data after certain time.
Step D
ID Bits in 3rd child unit are set to 3.
3rd Child outputs Startbit (LOW), 16bit Data, Stopbit (HIGH) through SD.
This data is written to SIOMULTI3 of all GBAs (including 3rd child).
Transfer ends (this was the last child).
Transfer end
Master sets SC=HIGH, all GBAs set SO=HIGH.
The Start/Busy bits of all GBAs are automatically cleared.
Interrupts are requested in all GBAs (as far as enabled).
This bit is set when a slave did not receive SI=LOW even though SC=LOW signalized a transfer (this might happen when connecting more than 4 GBAs, or when the previous child is not connected). Also, the bit is set when a Stopbit wasn’t HIGH.
The error bit may be undefined during active transfer - read only after transfer completion (the transfer continues and completes as normal even if errors have occurred for some or all GBAs).
Don’t know: The bit is automatically reset/initialized with each transfer, or must be manually reset?
The transmission time depends on the selected Baud rate. And on the amount of Bits (16 data bits plus start/stop bits for each GBA), delays between data for each GBA, plus final timeout (if less than 4 GBAs). That is, depending on the number of connected GBAs:
GBAs Bits Delays Timeout
1 18 None Yes
2 36 1 Yes
3 54 2 Yes
4 72 3 None
(The average Delay and Timeout periods are unknown?)
Above is not counting the additional CPU time that must be spent on initiating and processing each transfer.
Beside for the actual SIO Multiplayer mode, you can also use SIO Normal mode for fast one-way data transfer from Master unit to all Child unit(s). See chapter about SIO Normal mode for details.
This mode works much like a RS232 port, however, the voltages are unknown, probably 0/3V rather than +/-12V ?. SI and SO are data lines (with crossed wires), SC and SD signalize Clear to Send (with crossed wires also, which requires special cable when linking between two GBAs ?)
Bit Expl.
0-3 Undocumented (current SC,SD,SI,SO state, as for General Purpose mode)
4-8 Not used (Should be 0, bits are read/write-able though)
9-13 Not used (Always 0, read only)
14 Not used (Should be 0, bit is read/write-able though)
15 Must be zero (0) for Normal/Multiplayer/UART modes
Bit Expl.
0-1 Baud Rate (0-3: 9600,38400,57600,115200 bps)
2 CTS Flag (0=Send always/blindly, 1=Send only when SC=LOW)
3 Parity Control (0=Even, 1=Odd)
4 Send Data Flag (0=Not Full, 1=Full) (Read Only)
5 Receive Data Flag (0=Not Empty, 1=Empty) (Read Only)
6 Error Flag (0=No Error, 1=Error) (Read Only)
7 Data Length (0=7bits, 1=8bits)
8 FIFO Enable Flag (0=Disable, 1=Enable)
9 Parity Enable Flag (0=Disable, 1=Enable)
10 Send Enable Flag (0=Disable, 1=Enable)
11 Receive Enable Flag (0=Disable, 1=Enable)
12 Must be "1" for UART mode
13 Must be "1" for UART mode
14 IRQ Enable (0=Disable, 1=IRQ when any Bit 4/5/6 become set)
15 Not used (Read only, always 0)
Addresses the send/receive shift register, or (when FIFO is used) the send/receive FIFO. In either case only the lower 8bit of SIODATA8 are used, the upper 8bit are not used.
The send/receive FIFO may store up to four 8bit data units each. For example, while 1 unit is still transferred from the send shift register, it is possible to deposit another 4 units in the send FIFO, which are then automatically moved to the send shift register one after each other.
The receiver outputs SD=LOW (which is input as SC=LOW at the remote side) when it is ready to receive data (that is, when Receive Enable is set, and the Receive shift register (or receive FIFO) isn’t full.
When CTS flag is set to always/blindly, then the sender transmits data immediately when Send Enable is set, otherwise data is transmitted only when Send Enable is set and SC is LOW.
The error flag is set when a bad stop bit has been received (stop bit must be 0), when a parity error has occurred (if enabled), or when new data has been completely received while the receive data register (or receive FIFO) is already full.
The error flag is automatically reset when reading from SIOCNT register.
The content of the FIFO is reset when FIFO is disabled in UART mode, thus, when entering UART mode initially set FIFO=disabled.
The Send/Receive enable bits must be reset before switching from UART mode into another SIO mode!
This communication mode uses Nintendo’s standardized JOY Bus protocol. When using this communication mode, the GBA is always operated as SLAVE!
In this mode, SI and SO pins are data lines (apparently synchronized by Start/Stop bits?), SC and SD are set to low (including during active transfer?), the transfer rate is unknown?
Bit Expl.
0-3 Undocumented (current SC,SD,SI,SO state, as for General Purpose mode)
4-8 Not used (Should be 0, bits are read/write-able though)
9-13 Not used (Always 0, read only)
14 Must be "1" for JOY BUS Mode
15 Must be "1" for JOY BUS Mode
This register is not used in JOY BUS mode.
Bit Expl.
0 Device Reset Flag (Command FFh) (Read/Acknowledge)
1 Receive Complete Flag (Command 14h or 15h?) (Read/Acknowledge)
2 Send Complete Flag (Command 15h or 14h?) (Read/Acknowledge)
3-5 Not used
6 IRQ when receiving a Device Reset Command (0=Disable, 1=Enable)
7-31 Not used
Bit 0-2 are working much like the bits in the IF register: Write a “1” bit to reset (acknowledge) the respective bit.
UNCLEAR: Interrupts can be requested for Send/Receive commands also?
Send/receive data registers.
Bit Expl.
0 Not used
1 Receive Status Flag (0=Remote GBA is/was receiving) (Read Only?)
2 Not used
3 Send Status Flag (1=Remote GBA is/was sending) (Read Only?)
4-5 General Purpose Flag (Not assigned, may be used for whatever purpose)
6-31 Not used
Bit 1 is automatically set when writing to local JOY_TRANS.
Bit 3 is automatically reset when reading from local JOY_RECV.
Below are the four possible commands which can be received by the GBA. Note that the GBA (slave) cannot send any commands itself, all it can do is to read incoming data, and to provide ‘reply’ data which may (or may not) be read out by the master unit.
Receive FFh (Command)
Send 00h (GBA Type number LSB (or MSB?))
Send 04h (GBA Type number MSB (or LSB?))
Send XXh (lower 8bits of SIOSTAT register)
Receive 00h (Command)
Send 00h (GBA Type number LSB (or MSB?))
Send 04h (GBA Type number MSB (or LSB?))
Send XXh (lower 8bits of SIOSTAT register)
Receive 15h (Command)
Receive XXh (Lower 8bits of JOY_RECV_L)
Receive XXh (Upper 8bits of JOY_RECV_L)
Receive XXh (Lower 8bits of JOY_RECV_H)
Receive XXh (Upper 8bits of JOY_RECV_H)
Send XXh (lower 8bits of SIOSTAT register)
Receive 14h (Command)
Send XXh (Lower 8bits of JOY_TRANS_L)
Send XXh (Upper 8bits of JOY_TRANS_L)
Send XXh (Lower 8bits of JOY_TRANS_H)
Send XXh (Upper 8bits of JOY_TRANS_H)
Send XXh (lower 8bits of SIOSTAT register)
In this mode, the SIO is ‘misused’ as a 4bit bi-directional parallel port, each of the SI,SO,SC,SD pins may be directly controlled, each can be separately declared as input (with internal pull-up) or as output signal.
Interrupts can be requested when SI changes from HIGH to LOW, as General Purpose mode does not require a serial shift clock, this interrupt may be produced even when the GBA is in Stop (low power standby) state.
Bit Expl.
0 SC Data Bit (0=Low, 1=High)
1 SD Data Bit (0=Low, 1=High)
2 SI Data Bit (0=Low, 1=High)
3 SO Data Bit (0=Low, 1=High)
4 SC Direction (0=Input, 1=Output)
5 SD Direction (0=Input, 1=Output)
6 SI Direction (0=Input, 1=Output, but see below)
7 SO Direction (0=Input, 1=Output)
8 SI Interrupt Enable (0=Disable, 1=Enable)
9-13 Not used
14 Must be "0" for General-Purpose Mode
15 Must be "1" for General-Purpose or JOYBUS Mode
SI should be always used as Input to avoid problems with other hardware which does not expect data to be output there.
This register is not used in general purpose mode. That is, the separate bits of SIOCNT still exist and are read- and/or write-able in the same manner as for Normal, Multiplay, or UART mode (depending on SIOCNT Bit 12,13), but are having no effect on data being output to the link port.
R.15 R.14 S.13 S.12 Mode
0 x 0 0 Normal 8bit
0 x 0 1 Normal 32bit
0 x 1 0 Multiplay 16bit
0 x 1 1 UART (RS232)
1 0 x x General Purpose
1 1 x x JOY BUS
Bit 0 1 2 3 4 5 6 7 8 9 10 11
Normal Master Rate SI/In SO/Out - - - Start - - - -
Multi Baud Baud SI/In SD/In ID# Err Start - - - -
UART Baud Baud CTS Parity S R Err Bits FIFO Parity Send Recv
bit Generations series (Japan only)
Boktai 2: Solar Boy Django (Konami)
Boktai 3: Sabata's Counterattack
Classic NES Series: Donkey Kong
Classic NES Series: Dr. Mario
Classic NES Series: Ice Climber
Classic NES Series: Pac-Man
Classic NES Series: Super Mario Bros.
Classic NES Series: Xevious
Digimon Racing (Bandai) (No Wireless Adapter support in European release)
Dragon Ball Z: Buu's Fury (Atari)
Famicom Mini Series: #13 Balloon Fight
Famicom Mini Series: #12 Clu Clu Land
Famicom Mini Series: #16 Dig Dug
Famicom Mini Series: #02 Donkey Kong
Famicom Mini Series: #15 Dr. Mario
Famicom Mini Series: #03 Ice Climber
Famicom Mini Series: #18 Makaimura
Famicom Mini Series: #08 Mappy
Famicom Mini Series: #11 Mario Bros.
Famicom Mini Series: #06 Pac-Man
Famicom Mini Series: #30 SD Gundam World Scramble Wars
Famicom Mini Series: #01 Super Mario Bros.
Famicom Mini Series: #21 Super Mario Bros.
Famicom Mini Series: #19 Twin Bee
Famicom Mini Series: #14 Wrecking Crew
Famicom Mini Series: #07 Xevious
Hamtaro: Ham-Ham Games (Nintendo)
Lord of the Rings: The Third Age, The (EA Games)
Mario Golf: Advance Tour (Nintendo)
Mario Tennis: Power Tour (Nintendo)
Mega Man Battle Network 5: Team Protoman (Capcom)
Mega Man Battle Network 5: Team Colonel (Capcom)
Mega Man Battle Network 6: Cybeast Falzar
Mega Man Battle Network 6: Cybeast Gregar
Momotaro Dentetsu G: Make a Gold Deck! (Japan only)
Pokemon Emerald (Nintendo)
Pokemon FireRed (Nintendo)
Pokemon LeafGreen (Nintendo)
Sennen Kazoku (Japan only)
Shrek SuperSlam
Sonic Advance 3
rcnt=8000h ;\
rcnt=80A0h ;
rcnt=80A2h ; reset adapter or so
wait ;
rcnt=80A0h ;/
siocnt=5003h ;\set 32bit normal mode, 2MHz internal clock
rcnt=0000h ;/
passes=0, index=0
@@lop:
passes=passes+1, if passes>32 then ERROR ;give up (usually only 10 passses)
recv.lo=siodata AND FFFFh ;response from adapter
recv.hi=siodata/10000h ;adapter's own "NI" data
if send.hi<>recv.lo then index=0, goto @@stuck ;<-- fallback to index=0
if (send.lo XOR FFFFh)<>recv.lo then goto @@stuck
if (send.hi XOR FFFFh)<>recv.hi then goto @@stuck
index=index+1
@@stuck:
send.lo=halfword[@@key_string+index*2]
send.hi=recv.hi XOR FFFFh
siodata=send.lo+(send.hi*10000h)
siocnt.bit7=1 ;<-- start transmission
if index<4 then goto @@lop
ret
@@key_string db 'NINTENDO',01h,80h ;10 bytes (5 halfwords; index=0..4)
GBA ADAPTER
xxxx494E ;\ <--> xxxxxxxx
xxxx494E ; "NI" <--> "NI"/; 494EB6B1 ;\
NOT("NI") /; B6B1494E ;/ <--> \; 494EB6B1 ; NOT("NI")
\; B6B1544E ;\"NT" <--> "NT"/; 544EB6B1 ;/
NOT("NT") /; ABB1544E ;/ <--> \; 544EABB1 ;\NOT("NT")
\; ABB14E45 ;\"EN" <--> "EN"/; 4E45ABB1 ;/
NOT("EN") /; B1BA4E45 ;/ <--> \; 4E45B1BA ;\NOT("EN")
\; B1BA4F44 ;\"DO" <--> "DO"/; 4F44B1BA ;/
NOT("DO") /; B0BB4F44 ;/ <--> \; 4F44B0BB ;\NOT("DO")
\; B0BB8001 ;-fin <--> fin-; 8001B0BB ;/
\ \ \ \
\ LSBs=Own \ LSBs=Inverse of
\ Data.From.Gba \ Prev.Data.From.Gba
\ \
MSBs=Inverse of MSBs=Own
Prev.Data.From.Adapter Data.From.Adapter
GBA Adapter
9966ppcch 80000000h ;-send command (cc), and num param_words (pp)
<param01> 80000000h ;\
<param02> 80000000h ; send "pp" parameter word(s), if any
... ... ;/
80000000h 9966rraah ;-recv ack (aa=cc+80h), and num response_words (rr)
80000000? <reply01> ;\
80000000? <reply02> ; recv "rr" response word(s), if any
... ... ;/
Wireless 32bit Transfers
wait until [4000128h].Bit2=0 ;want SI=0
set [4000128h].Bit3=1 ;set SO=1
wait until [4000128h].Bit2=1 ;want SI=1
set [4000128h].Bit3=0,Bit7=1 ;set SO=0 and start 32bit transfer
All command/param/reply transfers should be done at Internal Clock (except, Response Words for command 25h,27h,35h,37h should use External Clock).
Cmd Para Reply Name
10h - - Hello (send immediately after login)
11h - 1 Good/Bad response to cmd 16h ?
12h
13h - 1
14h
15h
16h 6 - Introduce (send game/user name)
17h 1 - Config (send after Hello) (eg. param=003C0420h or 003C043Ch)
18h
19h
1Ah
1Bh
1Ch - -
1Dh - NN Get Directory? (receive list of game/user names?)
1Eh - NN Get Directory? (receive list of game/user names?)
1Fh 1 - Select Game for Download (send 16bit Game_ID)
20h - 1
21h - 1 Good/Bad response to cmd 1Fh ?
22h
23h
24h - -
25h ;use EXT clock!
26h - -
27h - - Begin Download ? ;use EXT clock!
28h
29h
2Ah
2Bh
2Ch
2Dh
2Eh
2Fh
30h 1 -
31h
32h
33h
34h
35h ;use EXT clock!
36h
37h ;use EXT clock!
38h
39h
3Ah
3Bh
3Ch
3Dh - - Bye (return to language select)
3Eh
3Fh
Special Response 996601EEh for error or so? (only at software side?)
Main Chipset
U1 32pin Freescale MC13190 (2.4 GHz ISM band transceiver)
U2 48pin Freescale CT3000 or CT3001 (depending on adapter version)
X3 2pin 9.5MHz crystal
The MC13190 is a Short-Range, Low-Power 2.4 GHz ISM band transceiver.
The processor is Motorola’s 32-bit M-Core RISC engine. (?) MCT3000 (?)
See also:
Version with GERMAN Postal Code on sticker:
Sticker on Case:
"GAME BOY advance, WIRELESS ADAPTER"
"Pat.Pend.Made in Philipines, CE0125(!)B"
"MODEL NO./MODELE NO.AGB-015 D-63760 Grossosteim P/AGB-A-WA-EUR-2 E3"
PCB: "19-C046-04, A-7" (top side) and "B-7" and Microchip ",\\" (bottom side)
PCB: white stamp "3104, 94V-0, RU, TW-15"
PCB: black stamp "22FDE"
U1 32pin "Freescale 13190, 4WFQ" (MC13190) (2.4 GHz ISM band transceiver)
U2 48pin "Freescale CT3001, XAC0445" (bottom side)
X3 2pin "D959L4I" (9.5MHz) (top side) (ca. 19 clks per 2us)
Further components… top side (A-7)
D1 5pin "D6F, 44" (top side, below X3)
U71 6pin ".., () 2" (top side, right of X3, tiny black chip)
B71 6pin "[]" (top side, right of X3, small white chip)
ANT 2pin on-board copper wings
Q? 3pin (top side, above CN1)
Q? 3pin (top side, above CN1)
D? 2pin "72" (top side, above CN1)
D3 2pin "F2" (top side, above CN1)
U200 4pin "MSV" (top side, above CN1)
U202 5pin "LXKA" (top side, right of CN1)
U203 4pin "M6H" (top side, right of CN1)
CN1 6pin connector to GBA link port (top side)
Further components… bottom side (B-7)
U201 5pin "LXVB" (bottom side, near CN1)
U72 4pin "BMs" (bottom side, near ANT, tiny black chip)
FL70 ?pin "[] o26" (bottom side, near ANT, bigger white chip)
B70 6pin "[]" (bottom side, near ANT, small white chip)
Plus, resistors and capacitors (without any markings).
Version WITHOUT sticker:
Sticker on Case: N/A
PCB: "19-C046-03, A-1" (top side) and "B-1" and Microchip ",\\" (bottom side)
PCB: white stamp "3204, TW-15, RU, 94V-0"
PCB: black stamp "23MN" or "23NH" or so (smeared)
U1 32pin "Freescale 13190, 4FGD" (top side)
U2 48pin "Freescale CT3000, XAB0425" (bottom side) ;CT3000 (not CT3001)
X3 2pin "9.5SKSS4GT" (top side)
Further components… top side (A-1)
D1 5pin "D6F, 31" (top side, below X3)
U71 6pin "P3, () 2" (top side, right of X3, tiny black chip)
B71 6pin "[]" (top side, right of X3, small white chip)
ANT 2pin on-board copper wings
Q70 3pin (top side, above CN1)
D? 2pin "72" (top side, above CN1)
D3 2pin "F2" (top side, above CN1)
U200 4pin "MSV" (top side, above CN1)
U202 5pin "LXKH" (top side, right of CN1)
U203 4pin "M6H" (top side, right of CN1)
CN1 6pin connector to GBA link port (top side)
Further components… bottom side (B-1)
U201 5pin "LXV2" (bottom side, near CN1)
U70 6pin "AAG" (bottom side, near ANT, tiny black chip)
FL70 ?pin "[] o26" (bottom side, near ANT, bigger white chip)
B70 6pin "[]" (bottom side, near ANT, small white chip)
Plus, resistors and capacitors (without any markings).
Major Differences
Sticker "N/A" vs "Grossosteim P/AGB-A-WA-EUR-2 E3"
PCB-markings "19-C046-03, A-1, 3204" vs "19-C046-04, A-7, 3104"
U1 "CT3000, XAB0425" vs "CT3001, XAC0445"
Transistors One transistor (Q70) vs Two transistors (both nameless)
U70/U72 U70 "AAG" (6pin) vs U72 "BMs" (4pin)
Purpose of the changes is unknown (either older/newer revisions, or different regions with different FCC regulations).
Early GBA prototypes have been intended to include a built-in IR port for sending and receiving IR signals. Among others, this port could have been used to communicate with other GBAs, or older CGB models, or TV Remote Controls, etc.
[ THE INFRARED COMMUNICATION FEATURE IS -NOT- SUPPORTED ANYMORE ]
Anyways, the prototype specifications have been as shown below…
Keep in mind that the IR signal may be interrupted by whatever objects moved between sender and receiver - the IR port isn’t recommended for programs that require realtime data exchange (such like action games).
Bit Expl.
0 Transmission Data (0=LED Off, 1=LED On)
1 READ Enable (0=Disable, 1=Enable)
2 Reception Data (0=None, 1=Signal received) (Read only)
3 AMP Operation (0=Off, 1=On)
4 IRQ Enable Flag (0=Disable, 1=Enable)
5-15 Not used
When IRQ is enabled, an interrupt is requested if the incoming signal was 0.119us Off (2 cycles), followed by 0.536us On (9 cycles) - minimum timing periods each.
When transmitting an IR signal, note that it’d be not a good idea to keep the LED turned On for a very long period (such like sending a 1 second synchronization pulse). The recipient’s circuit would treat such a long signal as “normal IR pollution which is in the air” after a while, and thus ignore the signal.
Received data is internally latched. Latched data may be read out by setting both READ and AMP bits.
Note: Provided that you don’t want to receive your own IR signal, be sure to set Bit 0 to zero before attempting to receive data.
After using the IR port, be sure to reset the register to zero in order to reduce battery power consumption.
The built-in GBA gamepad has 4 direction keys, and 6 buttons.
Bit Expl.
0 Button A (0=Pressed, 1=Released)
1 Button B (etc.)
2 Select (etc.)
3 Start (etc.)
4 Right (etc.)
5 Left (etc.)
6 Up (etc.)
7 Down (etc.)
8 Button R (etc.)
9 Button L (etc.)
10-15 Not used
It’d be usually recommended to read-out this register only once per frame, and to store the current state in memory. As a side effect, this method avoids problems caused by switch bounce when a key is newly released or pressed.
The keypad IRQ function is intended to terminate the very-low-power Stop mode, it is not suitable for processing normal user input, to do this, most programs are invoking their keypad handlers from within VBlank IRQ.
Bit Expl.
0 Button A (0=Ignore, 1=Select)
1 Button B (etc.)
2 Select (etc.)
3 Start (etc.)
4 Right (etc.)
5 Left (etc.)
6 Up (etc.)
7 Down (etc.)
8 Button R (etc.)
9 Button L (etc.)
10-13 Not used
14 IRQ Enable Flag (0=Disable, 1=Enable)
15 IRQ Condition (0=Logical OR, 1=Logical AND)
In logical OR mode, an interrupt is requested when at least one of the selected buttons is pressed.
In logical AND mode, an interrupt is requested when ALL of the selected buttons are pressed.
In 8bit gameboy compatibility mode, L and R Buttons are used to toggle the screen size between normal 160x144 pixels and stretched 240x144 pixels.
The GBA SP is additionally having a * Button used to toggle the backlight on and off (controlled by separate hardware logic, there’s no way to detect or change the current backlight state by software).
Bit Expl.
0 Disable all interrupts (0=Disable All, 1=See IE register)
1-31 Not used
Bit Expl.
0 LCD V-Blank (0=Disable)
1 LCD H-Blank (etc.)
2 LCD V-Counter Match (etc.)
3 Timer 0 Overflow (etc.)
4 Timer 1 Overflow (etc.)
5 Timer 2 Overflow (etc.)
6 Timer 3 Overflow (etc.)
7 Serial Communication (etc.)
8 DMA 0 (etc.)
9 DMA 1 (etc.)
10 DMA 2 (etc.)
11 DMA 3 (etc.)
12 Keypad (etc.)
13 Game Pak (external IRQ source) (etc.)
14-15 Not used
Note that there is another ‘master enable flag’ directly in the CPUs Status Register (CPSR) accessible in privileged modes, see CPU reference for details.
Bit Expl.
0 LCD V-Blank (1=Request Interrupt)
1 LCD H-Blank (etc.)
2 LCD V-Counter Match (etc.)
3 Timer 0 Overflow (etc.)
4 Timer 1 Overflow (etc.)
5 Timer 2 Overflow (etc.)
6 Timer 3 Overflow (etc.)
7 Serial Communication (etc.)
8 DMA 0 (etc.)
9 DMA 1 (etc.)
10 DMA 2 (etc.)
11 DMA 3 (etc.)
12 Keypad (etc.)
13 Game Pak (external IRQ source) (etc.)
14-15 Not used
Interrupts must be manually acknowledged by writing a “1” to one of the IRQ bits, the IRQ bit will then be cleared.
“[Cautions regarding clearing IME and IE]
A corresponding interrupt could occur even while a command to clear IME or each flag of the IE register is being executed. When clearing a flag of IE, you need to clear IME in advance so that mismatching of interrupt checks will not occur.” ?
“[When multiple interrupts are used]
When the timing of clearing of IME and the timing of an interrupt agree, multiple interrupts will not occur during that interrupt. Therefore, set (enable) IME after saving IME during the interrupt routine.” ?
Upon interrupt execution, the CPU is switched into IRQ mode, and the physical interrupt vector is called - as this address is located in BIOS ROM, the BIOS will always execute the following code before it forwards control to the user handler:
00000018 b 128h ;IRQ vector: jump to actual BIOS handler
00000128 stmfd r13!,r0-r3,r12,r14 ;save registers to SP_irq
0000012C mov r0,4000000h ;ptr+4 to 03FFFFFC (mirror of 03007FFC)
00000130 add r14,r15,0h ;retadr for USER handler $+8=138h
00000134 ldr r15,[r0,-4h] ;jump to [03FFFFFC] USER handler
00000138 ldmfd r13!,r0-r3,r12,r14 ;restore registers from SP_irq
0000013C subs r15,r14,4h ;return from IRQ (PC=LR-4, CPSR=SPSR)
As shown above, a pointer to the 32bit/ARM-code user handler must be setup in [03007FFCh]. By default, 160 bytes of memory are reserved for interrupt stack at 03007F00h-03007F9Fh.
If necessary switch to THUMB state manually (handler is called in ARM state)
Determine reason(s) of interrupt by examining IF register
User program may freely assign priority to each reason by own logic
Process the most important reason of your choice
User MUST manually acknowledge by writing to IF register
If user wants to allow nested interrupts, save SPSR_irq, then enable IRQs.
If using other registers than BIOS-pushed R0-R3, manually save R4-R11 also.
Note that Interrupt Stack is used (which may have limited size)
So, for memory consuming stack operations use system mode (=user stack).
When calling subroutines in system mode, save LSR_usr also.
Restore SPSR_irq and/or R4-R11 if you’ve saved them above.
Finally, return to BIOS handler by BX LR (R14_irq) instruction.
Addr. Size Expl.
3007FFCh 4 Pointer to user IRQ handler (32bit ARM code)
3007FF8h 2 Interrupt Check Flag (for IntrWait/VBlankIntrWait functions)
3007FF4h 4 Allocated Area
3007FF0h 4 Pointer to Sound Buffer
3007FE0h 16 Allocated Area
3007FA0h 64 Default area for SP_svc Supervisor Stack (4 words/time)
3007F00h 160 Default area for SP_irq Interrupt Stack (6 words/time)
Memory below 7F00h is free for User Stack and user data. The three stack pointers are initially initialized at the TOP of the respective areas:
SP_svc=03007FE0h
SP_irq=03007FA0h
SP_usr=03007F00h
The user may redefine these addresses and move stacks into other locations, however, the addresses for system data at 7FE0h-7FFFh are fixed.
Registers R8-R12_fiq, R13_fiq, R14_fiq, SPSR_fiq
Registers R13-R14_abt, SPSR_abt
Registers R13-R14_und, SPSR_und
The ARM CPU provides two interrupt sources, IRQ and FIQ. In the GBA only IRQ is used. In normal GBAs, the FIQ signal is shortcut to VDD35, ie. the signal is always high, and there is no way to generate a FIQ by hardware. The registers R8..12_fiq could be used by software (when switching into FIQ mode by writing to CPSR) - however, this might make the game incompatible with hardware debuggers (which are reportedly using FIQs for debugging purposes).
This register is used to configure game pak access timings. The game pak ROM is mirrored to three address regions at 08000000h, 0A000000h, and 0C000000h, these areas are called Wait State 0-2. Different access timings may be assigned to each area (this might be useful in case that a game pak contains several ROM chips with different access times each).
Bit Expl.
0-1 SRAM Wait Control (0..3 = 4,3,2,8 cycles)
2-3 Wait State 0 First Access (0..3 = 4,3,2,8 cycles)
4 Wait State 0 Second Access (0..1 = 2,1 cycles)
5-6 Wait State 1 First Access (0..3 = 4,3,2,8 cycles)
7 Wait State 1 Second Access (0..1 = 4,1 cycles; unlike above WS0)
8-9 Wait State 2 First Access (0..3 = 4,3,2,8 cycles)
10 Wait State 2 Second Access (0..1 = 8,1 cycles; unlike above WS0,WS1)
11-12 PHI Terminal Output (0..3 = Disable, 4.19MHz, 8.38MHz, 16.78MHz)
13 Not used
14 Game Pak Prefetch Buffer (Pipe) (0=Disable, 1=Enable)
15 Game Pak Type Flag (Read Only) (0=GBA, 1=CGB) (IN35 signal)
16-31 Not used
At startup, the default setting is 0000h. Currently manufactured cartridges are using the following settings: WS0/ROM=3,1 clks; SRAM=8 clks; WS2/EEPROM: 8,8 clks; prefetch enabled; that is, WAITCNT=4317h, for more info see “GBA Cartridges” chapter.
First Access (Non-sequential) and Second Access (Sequential) define the waitstates for N and S cycles, the actual access time is 1 clock cycle PLUS the number of waitstates.
GamePak uses 16bit data bus, so that a 32bit access is split into TWO 16bit accesses (of which, the second fragment is always sequential, even if the first fragment was non-sequential).
NOTES:
The GBA forcefully uses non-sequential timing at the beginning of each 128K-block of gamepak ROM, eg. “LDMIA [801fff8h],r0-r7” will have non-sequential timing at 8020000h.
The PHI Terminal output (PHI Pin of Gamepak Bus) should be disabled.
After initial reset, the GBA BIOS initializes the register to 01h, and any further execution of the Reset vector (00000000h) will pass control to the Debug vector (0000001Ch) when sensing the register to be still set to 01h.
Bit Expl.
0 Undocumented. First Boot Flag (0=First, 1=Further)
1-7 Undocumented. Not used.
Normally the debug handler rejects control unless it detects Debug flags in cartridge header, in that case it may redirect to a cut-down boot procedure (bypassing Nintendo logo and boot delays, much like nocash burst boot for multiboot software). I am not sure if it is possible to reset the GBA externally without automatically resetting register 300h though.
Writing to this register switches the GBA into battery saving mode.
In Halt mode, the CPU is paused as long as (IE AND IF)=0, this should be used to reduce power-consumption during periods when the CPU is waiting for interrupt events.
In Stop mode, most of the hardware including sound and video are paused, this very-low-power mode could be used much like a screensaver.
Bit Expl.
0-6 Undocumented. Not used.
7 Undocumented. Power Down Mode (0=Halt, 1=Stop)
The current GBA BIOS addresses only the upper eight bits of this register (by writing 00h or 80h to address 04000301h), however, as the register isn’t officially documented, some or all of the bits might have different meanings in future GBA models.
For best forwards compatibility, it’d generally be more recommended to use the BIOS Functions SWI 2 (Halt) or SWI 3 (Stop) rather than writing to this register directly.
The BIOS writes the 8bit value 0FFh to this address. Purpose Unknown.
Probably just another bug in the BIOS.
Supported by GBA and GBA SP only - NOT supported by DS (even in GBA mode).
Also supported by GBA Micro - but crashes on “overclocked” WRAM setting.
Initialized to 0D000020h (by hardware). Unlike all other I/O registers, this register is mirrored across the whole I/O area (in increments of 64K, ie. at 4000800h, 4010800h, 4020800h, …, 4FF0800h)
Bit Expl.
0 Disable 32K+256K WRAM (0=Normal, 1=Disable) (when off: empty/prefetch)
1-3 Unknown (Read/Write-able)
4 Unknown (Always zero, not used or write only)
5 Enable 256K WRAM (0=Disable, 1=Normal) (when off: mirror of 32K WRAM)
6-23 Unknown (Always zero, not used or write only)
24-27 Wait Control WRAM 256K (0-14 = 15..1 Waitstates, 15=Lockup)
28-31 Unknown (Read/Write-able)
The default value 0Dh in Bits 24-27 selects 2 waitstates for 256K WRAM (ie. 3/3/6 cycles 8/16/32bit accesses). The fastest possible setting would be 0Eh (1 waitstate, 2/2/4 cycles for 8/16/32bit), that works on GBA and GBA SP only, the GBA Micro locks up with that setting (it’s on-chip RAM is too slow, and works only with 2 or more waitstates).
Note: One cycle equals approx. 59.59ns (ie. 16.78MHz clock).
GamePak Prefetch can be enabled in WAITCNT register. When prefetch buffer is enabled, the GBA attempts to read opcodes from Game Pak ROM during periods when the CPU is not using the bus (if any). Memory access is then performed with 0 Waits if the CPU requests data which is already stored in the buffer. The prefetch buffer stores up to eight 16bit values.
The prefetch feature works only with <opcodes> fetched from GamePak ROM. Opcodes executed in RAM or BIOS are not affected by the prefetch feature (even if that opcodes read <data> from GamePak ROM).
For GamePak ROM opcodes, prefetch may occur in two situations:
1) opcodes with internal cycles (I) which do not change R15, shift/rotate
register-by-register, load opcodes (ldr,ldm,pop,swp), multiply opcodes
2) opcodes that load/store memory (ldr,str,ldm,stm,etc.)
When Prefetch is disabled, the Prefetch Disable Bug will occur for all
"Opcodes in GamePak ROM with Internal Cycles which do not change R15"
for those opcodes, the bug changes the opcode fetch time from 1S to 1N.
Note: Affected opcodes (with I cycles) are: Shift/rotate register-by-register opcodes, multiply opcodes, and load opcodes (ldr,ldm,pop,swp).
Aside from ROM, cartridges may also include one of the following backup medias, used to store game positions, highscore tables, options, or other data.
The first 192 bytes at 8000000h-80000BFh in ROM are used as cartridge header. The same header is also used for Multiboot images at 2000000h-20000BFh (plus some additional multiboot entries at 20000C0h and up).
Address Bytes Expl.
000h 4 ROM Entry Point (32bit ARM branch opcode, eg. "B rom_start")
004h 156 Nintendo Logo (compressed bitmap, required!)
0A0h 12 Game Title (uppercase ascii, max 12 characters)
0ACh 4 Game Code (uppercase ascii, 4 characters)
0B0h 2 Maker Code (uppercase ascii, 2 characters)
0B2h 1 Fixed value (must be 96h, required!)
0B3h 1 Main unit code (00h for current GBA models)
0B4h 1 Device type (usually 00h) (bit7=DACS/debug related)
0B5h 7 Reserved Area (should be zero filled)
0BCh 1 Software version (usually 00h)
0BDh 1 Complement check (header checksum, required!)
0BEh 2 Reserved Area (should be zero filled)
--- Additional Multiboot Header Entries ---
0C0h 4 RAM Entry Point (32bit ARM branch opcode, eg. "B ram_start")
0C4h 1 Boot mode (init as 00h - BIOS overwrites this value!)
0C5h 1 Slave ID Number (init as 00h - BIOS overwrites this value!)
0C6h 26 Not used (seems to be unused)
0E0h 4 JOYBUS Entry Pt. (32bit ARM branch opcode, eg. "B joy_start")
Note: With all entry points, the CPU is initially set into system mode.
Space for a single 32bit ARM opcode that redirects to the actual startaddress of the cartridge, this should be usually a “B <start>” instruction.
Note: This entry is ignored by Multiboot slave GBAs (in fact, the entry is then overwritten and redirected to a separate Multiboot Entry Point, as described below).
Contains the Nintendo logo which is displayed during the boot procedure. Cartridge won’t work if this data is missing or modified.
In detail: This area contains Huffman compression data (but excluding the compression header which is hardcoded in the BIOS, so that it’d be probably not possible to hack the GBA by producing de-compression buffer overflows).
A copy of the compression data is stored in the BIOS, the GBA will compare this data and lock-up itself if the BIOS data isn’t exactly the same as in the cartridge (or multiboot header). The only exception are the two entries below which are allowed to have variable settings in some bits.
This is part of the above Nintendo Logo area, and must be commonly set to 21h, however, Bit 2 and Bit 7 may be set to other values.
When both bits are set (ie. A5h), the FIQ/Undefined Instruction handler in the BIOS becomes unlocked, the handler then forwards these exceptions to the user handler in cartridge ROM (entry point defined in 80000B4h, see below).
Other bit combinations currently do not seem to have special functions.
This is part of the above Nintendo Logo area, and must be commonly set to F8h, however, Bit 0-1 may be set to other values.
During startup, the BIOS performs some dummy-reads from a stream of pre-defined addresses, even though these reads seem to be meaningless, they might be intended to unlock a read-protection inside of commercial cartridge. There are 16 pre-defined address streams - selected by a 4bit key number - of which the upper two bits are gained from 800009Eh Bit 0-1, and the lower two bits from a checksum across header bytes 09Dh..0B7h (bytewise XORed, divided by 40h).
Space for the game title, padded with 00h (if less than 12 chars).
This is the same code as the AGB-UTTD code which is printed on the package and sticker on (commercial) cartridges (excluding the leading “AGB-“ part).
U Unique Code (usually "A" or "B" or special meaning)
TT Short Title (eg. "PM" for Pac Man)
D Destination/Language (usually "J" or "E" or "P" or specific language)
The first character (U) is usually “A” or “B”, in detail:
A Normal game; Older titles (mainly 2001..2003)
B Normal game; Newer titles (2003..)
C Normal game; Not used yet, but might be used for even newer titles
F Famicom/Classic NES Series (software emulated NES games)
K Yoshi and Koro Koro Puzzle (acceleration sensor)
P e-Reader (dot-code scanner)
R Warioware Twisted (cartridge with rumble and z-axis gyro sensor)
U Boktai 1 and 2 (cartridge with RTC and solar sensor)
V Drill Dozer (cartridge with rumble)
The second/third characters (TT) are:
Usually an abbreviation of the game title (eg. "PM" for "Pac Man") (unless
that gamecode was already used for another game, then TT is just random)
The fourth character (D) indicates Destination/Language:
J Japan P Europe/Elsewhere F French S Spanish
E USA/English D German I Italian
Identifies the (commercial) developer. For example, “01”=Nintendo.
Must be 96h.
Identifies the required hardware. Should be 00h for current GBA models.
Normally, this entry should be zero. With Nintendo’s hardware debugger Bit 7 identifies the debugging handlers entry point and size of DACS (Debugging And Communication System) memory: Bit7=0: 9FFC000h/8MBIT DACS, Bit7=1: 9FE2000h/1MBIT DACS. The debugging handler can be enabled in 800009Ch (see above), normal cartridges do not have any memory (nor any mirrors) at these addresses though.
Reserved, zero filled.
Version number of the game. Usually zero.
Header checksum, cartridge won’t work if incorrect. Calculate as such:
chk=0:for i=0A0h to 0BCh:chk=chk-[i]:next:chk=(chk-19h) and 0FFh
Reserved, zero filled.
Below required for Multiboot/slave programs only. For Multiboot, the above 192 bytes are required to be transferred as header-block (loaded to 2000000h-20000BFh), and some additional header-information must be located at the beginning of the actual program/data-block (loaded to 20000C0h and up). This extended header consists of Multiboot Entry point(s) which must be set up correctly, and of two reserved bytes which are overwritten by the boot procedure:
This entry is used only if the GBA has been booted by using Normal or Multiplay transfer mode (but not by Joybus mode).
Typically deposit a ARM-32bit “B <start>” branch opcode at this location, which is pointing to your actual initialization procedure.
The slave GBA download procedure overwrites this byte by a value which is indicating the used multiboot transfer mode.
Value Expl.
01h Joybus mode
02h Normal mode
03h Multiplay mode
Typically set this byte to zero by inserting DCB 00h in your source.
Be sure that your uploaded program does not contain important program code or data at this location, or at the ID-byte location below.
If the GBA has been booted in Normal or Multiplay mode, this byte becomes overwritten by the slave ID number of the local GBA (that’d be always 01h for normal mode).
Value Expl.
01h Slave #1
02h Slave #2
03h Slave #3
Typically set this byte to zero by inserting DCB 00h in your source.
When booted in Joybus mode, the value is NOT changed and remains the same as uploaded from the master GBA.
Appears to be unused.
If the GBA has been booted by using Joybus transfer mode, then the entry point is located at this address rather than at 20000C0h. Either put your initialization procedure directly at this address, or redirect to the actual boot procedure by depositing a “B <start>” opcode here (either one using 32bit ARM code). Or, if you are not intending to support joybus mode (which is probably rarely used), ignore this entry.
The games F-ZERO and Super Mario Advance use ROMs of 4 MBytes each. Zelda uses 8 MBytes. Not sure if other sizes are manufactured.
The GBA starts the cartridge with 4,2 waitstates (N,S) and prefetch disabled. The program may change these settings by writing to WAITCNT, the games F-ZERO and Super Mario Advance use 3,1 waitstates (N,S) each, with prefetch enabled.
Third-party flashcards are reportedly running unstable with these settings. Also, prefetch and shorter waitstates are allowing to read more data and opcodes from ROM is less time, the downside is that it increases the power consumption.
Because of how 24bit addresses are squeezed through the Gampak bus, the cartridge must include a circuit that latches the lower 16 address bits on non-sequential access, and that increments these bits on sequential access. Nintendo includes this circuit directly in the ROM chip.
Also, the ROM must have 16bit data bus (or a circuit which converts two 8bit data units into one 16bit unit - by not exceeding the waitstate timings).
Nintendo didn’t include a backup-type entry in the ROM header, however, the required type can be detected by ID strings in the ROM-image. Nintendo’s tools are automatically inserting these strings (as part of their library headers). When using other tools, you may insert ID strings by hand.
The ID string must be located at a word-aligned memory location, the string length should be a multiple of 4 bytes (padded with zero’s).
EEPROM_Vnnn EEPROM 512 bytes or 8 Kbytes (4Kbit or 64Kbit)
SRAM_Vnnn SRAM 32 Kbytes (256Kbit)
FLASH_Vnnn FLASH 64 Kbytes (512Kbit) (ID used in older files)
FLASH512_Vnnn FLASH 64 Kbytes (512Kbit) (ID used in newer files)
FLASH1M_Vnnn FLASH 128 Kbytes (1Mbit)
For Nintendo’s tools, “nnn” is a 3-digit library version number. When using other tools, best keep it set to “nnn” rather than inserting numeric digits.
No$gba does auto-detect most backup types, even without ID strings, except for 128K FLASH (without ID “FLASH1M_Vnnn”, the FLASH size defaults to 64K). Ideally, for faster detection, the ID should be put into the first some bytes of the ROM-image (ie. somewhere right after the ROM header).
SRAM - 32 KBytes (256Kbit) Lifetime: Depends on back-up battery
FRAM - 32 KBytes (256Kbit) Lifetime: 10,000,000,000 read/write per bit
Hyundai GM76V256CLLFW10 SRAM (Static RAM) (eg. F-Zero)
Fujitsu MB85R256 FRAM (Ferroelectric RAM) (eg. Warioware Twisted)
SRAM/FRAM is mapped to E000000h-E007FFFh, it should be accessed with 8 waitstates (write a value of 3 into Bit0-1 of WAITCNT).
The SRAM/FRAM databus is restricted to 8 bits, it should be accessed by LDRB, LDRSB, and STRB opcodes only.
Reading from SRAM/FRAM should be performed by code executed in WRAM only (but not by code executed in ROM). There is no such restriction for writing.
The GBA SRAM/FRAM carts do not include a write-protect function (unlike older 8bit gameboy carts). This seems to be a problem and may cause data loss when a cartridge is removed or inserted while the GBA is still turned on. As far as I understand, this is not so much a hardware problem, but rather a software problem, ie. theoretically you could remove/insert the cartridge as many times as you want, but you should take care that your program does not crash (and write blindly into memory).
Enable the Gamepak Interrupt (it’ll most likely get triggered when removing the cartridge), and hang-up the GBA in an endless loop when your interrupt handler senses a Gamepak IRQ. For obvious reason, your interrupt handler should be located in WRAM, ie. not in the (removed) ROM cartridge. The handler should process Gamepak IRQs at highest priority. Periods during which interrupts are disabled should be kept as short as possible, if necessary allow nested interrupts.
A program that relies wholly on code and data in WRAM, and that does not crash even when ROM is removed, may keep operating without having to use the above mechanism.
Do NOT use the workaround for programs that run without a cartridge inserted (ie. single gamepak/multiboot slaves), or for programs that use Gamepak IRQ/DMA for other purposes.
All other programs should use it. It’d be eventually a good idea to include it even in programs that do not use SRAM/FRAM themselves (eg. otherwise removing a SRAM/FRAM-less cartridge may lock up the GBA, and may cause it to destroy backup data when inserting a SRAM/FRAM cartridge).
FRAM (Ferroelectric RAM) is a newer technology, used in newer GBA carts, unlike SRAM (Static RAM), it doesn’t require a battery to hold the data. At software side, it is accessed exactly like SRAM, ie. unlike EEPROM/FLASH, it doesn’t require any Write/Erase commands/delays.
In SRAM/FRAM cartridges, the /REQ pin (Pin 31 of Gamepak bus) should be a little bit shorter as than the other pins; when removing the cartridge, this causes the gamepak IRQ signal to get triggered before the other pins are disconnected.
9853 - EEPROM 512 Bytes (0200h) (4Kbit) (eg. used by Super Mario Advance)
9854 - EEPROM 8 KBytes (2000h) (64Kbit) (eg. used by Boktai)
Lifetime: 100,000 writes per address
The eeprom is connected to Bit0 of the data bus, and to the upper 1 bit (or upper 17 bits in case of large 32MB ROM) of the cartridge ROM address bus, communication with the chip takes place serially.
The eeprom must be used with 8 waitstates (set WAITCNT=X3XXh; 8,8 clks in WS2 area), the eeprom can be then addressed at DFFFF00h..DFFFFFFh.
Respectively, with eeprom, ROM is restricted to 8000000h-9FFFeFFh (max. 1FFFF00h bytes = 32MB minus 256 bytes). On carts with 16MB or smaller ROM, eeprom can be alternately accessed anywhere at D000000h-DFFFFFFh.
Data can be read from (or written to) the EEPROM in units of 64bits (8 bytes). Writing automatically erases the old 64bits of data. Addressing works in units of 64bits respectively, that is, for 512 Bytes EEPROMS: an address range of 0-3Fh, 6bit bus width; and for 8KByte EEPROMs: a range of 0-3FFh, 14bit bus width (only the lower 10 address bits are used, upper 4 bits should be zero).
Prepare the following bitstream in memory:
2 bits "11" (Read Request)
n bits eeprom address (MSB first, 6 or 14 bits, depending on EEPROM)
1 bit "0"
Then transfer the stream to eeprom by using DMA.
Read a stream of 68 bits from EEPROM by using DMA,
then decipher the received data as follows:
4 bits - ignore these
64 bits - data (conventionally MSB first)
Prepare the following bitstream in memory, then transfer the stream to eeprom by using DMA, it’ll take ca. 108368 clock cycles (ca. 6.5ms) until the old data is erased and new data is programmed.
2 bits "10" (Write Request)
n bits eeprom address (MSB first, 6 or 14 bits, depending on EEPROM)
64 bits data (conventionally MSB first)
1 bit "0"
After the DMA, keep reading from the chip, by normal LDRH [DFFFF00h], until Bit 0 of the returned data becomes “1” (Ready). To prevent your program from locking up in case of malfunction, generate a timeout if the chip does not reply after 10ms or longer.
Transferring a bitstreams to/from the EEPROM must be done via DMA3 (manual transfers via LDRH/STRH won’t work; probably because they don’t keep /CS=LOW and A23=HIGH throughout the transfer).
For using DMA, a buffer in memory must be used (that buffer would be typically allocated temporarily on stack, one halfword for each bit, bit1-15 of the halfwords are don’t care, only bit0 is used).
The buffer must be transfered as a whole to/from EEPROM by using DMA3 (DMA0-2 can’t access external memory), use 16bit transfer mode, both source and destination address incrementing (ie. DMA3CNT=80000000h+length).
DMA channels of higher priority should be disabled during the transfer (ie. H/V-Blank or Sound FIFO DMAs). And, of course any interrupts that might mess with DMA registers should be disabled.
The EEPROM chips are having only 8 pins, these are connected, Pin 1..8, to ROMCS, RD, WR, AD0, GND, GND, A23, VDD of the GamePak bus. Carts with 32MB ROM must have A7..A22 logically ANDed with A23.
There seems to be no autodection mechanism, so that a hardcoded bus width must be used.
64 KBytes - 512Kbits Flash ROM - Lifetime: 10,000 writes per sector
128 KBytes - 1Mbit Flash ROM - Lifetime: ??? writes per sector
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=90h (enter ID mode)
dev=[E000001h], man=[E000000h] (get device & manufacturer)
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=F0h (terminate ID mode)
Used to detect the type (and presence) of FLASH chips. See Device Types below.
dat=[E00xxxxh] (read byte from address xxxx)
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=80h (erase command)
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=10h (erase entire chip)
wait until [E000000h]=FFh (or timeout)
Erases all memory in chip, erased memory is FFh-filled.
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=80h (erase command)
[E005555h]=AAh, [E002AAAh]=55h, [E00n000h]=30h (erase sector n)
wait until [E00n000h]=FFh (or timeout)
Erases memory at E00n000h..E00nFFFh, erased memory is FFh-filled.
old=IME, IME=0 (disable interrupts)
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=A0h (erase/write sector command)
[E00xxxxh+00h..7Fh]=dat[00h..7Fh] (write 128 bytes)
IME=old (restore old IME state)
wait until [E00xxxxh+7Fh]=dat[7Fh] (or timeout)
Interrupts (and DMAs) should be disabled during command/write phase. Target address must be a multiple of 80h.
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=A0h (write byte command)
[E00xxxxh]=dat (write byte to address xxxx)
wait until [E00xxxxh]=dat (or timeout)
The target memory location must have been previously erased.
[E005555h]=F0h (force end of write/erase command)
Use if timeout occurred during “wait until” periods, for Macronix devices only.
[E005555h]=AAh, [E002AAAh]=55h, [E005555h]=B0h (select bank command)
[E000000h]=bnk (write bank number 0..1)
Specifies 64K bank number for read/write/erase operations.
Required because gamepak flash/sram addressbus is limited to 16bit width.
Nintendo puts different FLASH chips in commercial game cartridges. Developers should thus detect & support all chip types. For Atmel chips it’d be recommended to simulate 4K sectors by software, though reportedly Nintendo doesn’t use Atmel chips in newer games anymore. Also mind that different timings should not disturb compatibility and performance.
ID Name Size Sectors AverageTimings Timeouts/ms Waits
D4BFh SST 64K 16x4K 20us?,?,? 10, 40, 200 3,2
1CC2h Macronix 64K 16x4K ?,?,? 10,2000,2000 8,3
1B32h Panasonic 64K 16x4K ?,?,? 10, 500, 500 4,2
3D1Fh Atmel 64K 512x128 ?,?,? ...40.., 40 8,8
1362h Sanyo 128K ? ?,?,? ? ? ? ?
09C2h Macronix 128K ? ?,?,? ? ? ? ?
Identification Codes MSB=Device Type, LSB=Manufacturer.
Size in bytes, and numbers of sectors * sector size in bytes.
Average medium Write, Erase Sector, Erase Chips timings are unknown?
Timeouts in milliseconds for Write, Erase Sector, Erase Chips.
Waitstates for Writes, and Reads in clock cycles.
FLASH memory is located in the “SRAM” area at E000000h..E00FFFFh, which is restricted to 16bit address and 8bit data buswidths. Respectively, the memory can be accessed <only> by 8bit read/write LDRB/STRB opcodes.
Also, reading anything (data or status/busy information) can be done <only> by opcodes executed in WRAM (not from opcodes in ROM) (there’s no such restriction for writing).
Use 8 clk waitstates for initial detection (WAITCNT Bits 0,1 both set). After detection of certain device types smaller wait values may be used for write/erase, and even smaller wait values for raw reading, see Device Types table.
In practice, games seem to use smaller values only for write/erase (even though those operations are slow anyways), whilst raw reads are always done at 8 clk waits (even though reads could actually benefit slightly from smaller wait values).
Even though device signalizes the completion of write/erase operations, it’d be recommended to read/confirm the content of the changed memory area by software. In practice, Nintendo’s “erase-write-verify-retry” function typically repeats the operation up to three times in case of errors.
Also, for SST devices only, the “erase-write” and “erase-write-verify-retry” functions repeat the erase command up to 80 times, additionally followed by one further erase command if no retries were needed, otherwise followed by six further erase commands.
FLASH (64Kbytes) is used by the game Sonic Advance, and possibly others.
128 KBytes - 1Mbit DACS - Lifetime: 100,000 writes.
1024 KBytes - 8Mbit DACS - Lifetime: 100,000 writes.
DACS (Debugging And Communication System) is used in Nintendo’s hardware debugger only, DACS is NOT used in normal game cartridges.
Parts of DACS memory is used to store the debugging exception handlers (entry point/size defined in cartridge header), the remaining memory could be used to store game positions or other data. The address space is the upper end of the 32MB ROM area, the memory can be read directly by the CPU, including for ability to execute program code in this area.
4bit General Purpose I/O Port (GPIO) - contained in the ROM-chip
Used by Boktai for RTC and Solar Sensor:
GBA Cart Solar Sensor And by Warioware Twisted for Rumble and Z-Axis Sensor:
The I/O registers are mapped to a 6-byte region in the ROM-area at 80000C4h, the 6-byte region should be zero-filled in the ROM-image. In Boktai, the size of the zero-filled region is 0E0h bytes - that probably due to an incorrect definition (the additional bytes do not contain any extra ports, nor mirrors of the ports in the 6-byte region). Observe that ROM-bus writes are limited to 16bit/32bit access (STRB opcodes are ignored; that, only in DS mode?).
bit0-3 Data Bits 0..3 (0=Low, 1=High)
bit4-15 not used (0)
bit0-3 Direction for Data Port Bits 0..3 (0=In, 1=Out)
bit4-15 not used (0)
bit0 Register 80000C4h..80000C8h Control (0=Write-Only, 1=Read/Write)
bit1-15 not used (0)
In write-only mode, reads return 00h (or possible other data, if the rom contains non-zero data at that location).
GPIO | Boktai | Wario
Bit Pin | RTC SOL | GYR RBL
-----------+---------+---------
0 ROM.1 | SCK CLK | RES -
1 ROM.2 | SIO RST | CLK -
2 ROM.21 | CS - | DTA -
3 ROM.22 | - FLG | - MOT
-----------+---------+---------
IRQ ROM.43 | IRQ - | - -
Aside from the I/O Port, the ROM-chip also includes an inverter (used for inverting the RTC /IRQ signal), and some sort of an (unused) address decoder output (which appears to be equal or related to A23 signal) (ie. reacting on ROM A23, or SRAM D7, which share the same pin on GBA slot).
S3511 - 8pin RTC with 3-wire serial bus (used in Boktai)
The RTC chip is (almost) the same as used in NDS consoles:
DS Real-Time Clock (RTC) The chip is accessed via 4bit I/O port (only 3bits are used for RTC):
NDS_________GBA_________GBA/Params___
stat2 control (1-byte)
datetime datetime (7-byte)
time time (3-byte)
stat1 force reset (0-byte)
clkadjust force irq (0-byte)
alarm1/int1 always FFh (boktai contains code for writing 1-byte to it)
alarm2 always FFh (unused)
free always FFh (unused)
Bit Dir Expl.
0 - Not used
1 R/W IRQ duty/hold related?
2 - Not used
3 R/W Per Minute IRQ (30s duty) (0=Disable, 1=Enable)
4 - Not used
5 R/W Unknown?
6 R/W 12/24-hour Mode (0=12h, 1=24h) (usually 1)
7 R Power-Off (auto cleared on read) (0=Normal, 1=Failure)
Setting after Battery-Shortcut is 82h. Setting after Force-Reset is 00h.
Unused bits seem to be always zero, but might be read-only or write-only?
Same as NDS, except AM/PM flag moved from hour.bit6 (NDS) to hour.bit7 (GBA).
Used to reset all RTC registers (all used registers become 00h, except day/month which become 01h), or to drag the IRQ output LOW for a short moment. These registers are strobed by ANY access to them, ie. by both writing to, as well as reading from these registers.
The package has identical pin-outs as in NDS, although it is slightly larger than the miniature chip in the DS.
For whatever reason, the RTC’s /IRQ output is passed through an inverter (contained in the ROM-chip), the inverted signal is then passed to the /IRQ pin on the cartridge slot. So, IRQ’s will be triggered on the “wrong” edge - possible somehow in relation with detecting cartridge-removal IRQs?
Uses a Photo Diode as Solar Sensor (used in Boktai, allowing to defeat vampires when the cartridge is exposed to sunlight). The cartridge comes in transparent case, and it’s slightly longer than normal carts, so the sensor reaches out of the cartridge slot. According to the manual, the sensor works only with sunlight, but actually it works with any strong light source (eg. a 100 Watt bulb at 1-2 centimeters distance). The sensor is accessed via 4bit I/O port (only 3bits used), which is contained in the ROM-chip.
The cartridge uses a self-made digital-ramp converter A/D converter, which is (maybe) better than measuring a capacitor charge-up time, and/or less expensive than a real ADC-chip:
It contains a 74LV4040 12bit binary counter (clocked by CPU via the I/O port), of which only the lower 8bit are used, which are passed to a resistor ladder-type D/A converter, which is generating a linear increasing voltage, which is passed to a TLV272 voltage comparator, which is passing a signal to the I/O port when the counter voltage becomes greater than the sensor voltage.
strh 0001h,[80000c8h] ;-enable R/W mode
strh 0007h,[80000c6h] ;-init I/O direction
strh 0002h,[80000c4h] ;-reset counter to zero (high=reset) (I/O bit0)
strh 0000h,[80000c4h] ;-clear reset (low=normal)
mov r0,0 ;-initial level
@@lop:
strh 0001h,[80000c4h] ;-clock high ;\increase counter (I/O bit1)
strh 0000h,[80000c4h] ;-clock low ;/
ldrh r1,[80000c4h] ;-read port (I/O bit3)
tst r1,08h ;\
addeq r0,1 ; loop until voltage match (exit with r0=00h..FFh),
tsteq r0,100h ; or until failure/timeout (exit with r0=100h)
beq @@lop ;/
The results vary depending on the clock rate used. In above example, ensure that IRQs or DMAs do not interrupt the function. Alternately, use a super-slow clock rate (eg. like 666Hz used in Boktai) so that additional small IRQ/DMA delays have little effect on the overall timing. Results should be somewhat:
E8h total darkness (including LED light, or daylight on rainy days)
Dxh close to a 100 Watt Bulb
5xh reaches max level in boktai's solar gauge
00h close to a tactical nuclear bomb dropped on your city
The exact values may change from cartridge to cartridge, so it’d be recommened to include a darkness calibration function, prompting the user to cover the sensor for a moment (in Boktai, access Options by pressing left/right in title screen) (alternately, auto-calibration could theoretically memorize the darkest in-game level ever seen).
Yoshi’s Universal Gravitation / Yoshi Topsy Turvy (X/Y-Axis)
Koro Koro Puzzle (probably same as Yoshi, X/Y-Axis, too) (?)
All of the registers are one byte wide, mapped into the top “half” of the SRAM memory range.
E008000h (W) Write 55h to start sampling
E008100h (W) Write AAh to start sampling
E008200h (R) Lower 8 bits of X axis
E008300h (R) Upper 4 bits of X axis, and Bit7: ADC Status (0=Busy, 1=Ready)
E008400h (R) Lower 8 bits of Y axis
E008500h (R) Upper 4 bits of Y axis
You must set SRAM wait control to 8 clocks to access it correctly.
You must also set the cartridge PHI terminal to 4 MHz to make it work.
Sampling routine (typically executed once a frame during VBlank):
wait until [E008300h].Bit7=1 or until timeout ;wait ready
x = ([E008300h] AND 0Fh)*100h + [E008200h] ;get x
y = ([E008500h] AND 0Fh)*100h + [E008400h] ;get y
[E008000h]=55h, [E008100h]=AAh ;start next conversion
Example values (may vary on different carts and on temperature, etc):
X ranged between 0x2AF to 0x477, center at 0x392. Huh?
Y ranged between 0x2C3 to 0x480, center at 0x3A0. Huh?
Thanks to Flubba for Yoshi-Type information.
Unknown if the Yoshi-Type sensors are sensing rotation, or orientation, or motion, or something else? In case of rotation, rotation around X-axis would result in motion in Y-direction, so not too sure whether X and Y have which meaning?
Most probably, the sensors are measuring (both) static acceleration (gravity), and dynamic acceleration (eg. shaking the device left/right).
The X/Y values are likely to be mirrored depending on using a back-loading cartridge slot (original GBA), or front-loading cartridge slot (newer GBA SP, and NDS, and NDS-Lite).
Warioware Twisted (Z-Axis Gyro Sensor, plus Rumble)
Uses a single-axis sensor, which senses rotation around the Z-axis. The sensor is connected to an analogue-in, serial-out ADC chip, which is accessed via lower 3 bits of the GPIO,
GPIO.Bit0 (W) Start Conversion
GPIO.Bit1 (W) Serial Clock
GPIO.Bit2 (R) Serial Data
GPIO.Bit3 (W) Used for Rumble (not gyro related)
There should be at least <three sequential 32bit ARM opcodes executed in WS0 region> between the STRH opcodes which toggle the CLK signal. Wario uses WAITCNT=45B7h (SRAM=8clks, WS0/WS1/WS2=3,1clks, Prefetch=On, PHI=Off).
The data stream consists of: 4 dummy bits (usually zero), followed by 12 data bits, followed by endless unused bits (usually zero).
read_gyro:
mov r1,8000000h ;-cartridge base address
mov r0,01h ;\enable R/W access
strh r0,[r1,0c8h] ;/
mov r0,0bh ;\init direction (gpio2=input, others=output)
strh r0,[r1,0c6h] ;/
ldrh r2,[r1,0c4h] ;-get current state (for keeping gpio3=rumble)
orr r2,3 ;\
strh r2,[r1,0c4h] ;gpio0=1 ; start ADC conversion
bic r2,1 ;
strh r2,[r1,0c4h] ;gpio0=0 ;/
mov r0,00010000h ;stop-bit ;\
bic r2,2 ;
@@lop: ;
ldrh r3,[r1,0c4h] ;get gpio2=data ; read 16 bits
strh r2,[r1,0c4h] ;gpio1=0=clk=low ; (4 dummy bits, plus 12 data bits)
movs r3,r3,lsr 3 ;gpio2 to cy=data ;
adcs r0,r0,r0 ;merge data, cy=done;
orr r3,r2,2 ;set bit1 and delay ;
strh r3,[r1,0c4h] ;gpio1=1=clk=high ;
bcc @@lop ;/
bic r0,0f000h ;-strip upper 4 dummy bits (isolate 12bit adc)
bx lr
Example values (may vary on different carts, battery charge, temperature, etc):
354h rotated in anti-clockwise direction (shock-speed)
64Dh rotated in anti-clockwise direction (normal fast)
6A3h rotated in anti-clockwise direction (slow)
6C0h no rotation (stopped)
6DAh rotation in clockwise direction (slow)
73Ah rotation in clockwise direction (normal fast)
9E3h rotation in clockwise direction (shock-speed)
For detection, values 000h and FFFh would indicate that there’s no sensor.
The Z-axis always points into same direction; no matter of frontloading or backloading cartridge slots.
Thanks to Momo Vampire for contributing a Wario cartridge.
X-Axis and Y-Axis are meant to be following the screens X and Y coordinates, so the Z-Axis would point into the screens depth direction.
DSi consoles can mis-use the built-in cameras as Gyro sensor (as done by the System Flaw DSi game).
Warioware Twisted (Rumble, plus Z-Axis Gyro Sensor)
Drill Dozer (Rumble only) <– and ALSO supports Gameboy Player rumble?
GBA Rumble Carts are containing a small motor, which is causing some vibration when/while it is switched on (that, unlike DS Rumble, which must be repeatedly toggled on/off).
In Warioware Twisted, rumble is controlled via GPIO.Bit3 (Data 0=Low=Off, 1=High=On) (and Direction 1=Output), the other GPIO Bits are used for the gyro sensor.
Unknown if Drill Dozer is controlled via GPIO.Bit3, too?
Additionally, there’s a Rumble Pak for the NDS, which connects to the GBA slot, so it can be used also for GBA games (provided that the game doesn’t require the GBA slot, eg. GBA multiboot games).
Moreover, GBA games that are running on a Gameboy Player are having access to the Rumble function of Gamecube joypads.
________________
| ShortStrip |
|L L|
|o Center o|
|n Region n|
|g g|
| may contain |
|S pictures, S|
|t instructions t|
|r etc. r|
|i i|
|p p|
|___ShortStrip___|
The e-Reader is a large GBA cartridge (about as big as the GBA console), with built-in dotcode scanning hardware. Dotcodes are tiny strips of black and white pixels printed on the edges of cardboard cards. The cards have to be pulled through a slot on the e-Reader, which is giving it a feeling like using a magnet card reader. The binary data on the dotcodes contains small games, either in native GBA code (ARM/THUMB), or in software emulated 8bit Z80 or NES/Famicom (6502) code.
The hardware consists of regular 8MByte ROM and 128KByte FLASH chips, two link ports, a custom PGA chip, the camera module (with two red LEDs, used as light source), and some analogue components for generating the LED voltages, etc. The camera supports 402x302 pixels with 7bit monochrome color depth, but the PGA clips it to max 320 pixels per scanline with 1bit color depth.
The e-Reader’s two link ports are simply interconnected with each other; without connection to the rest of the e-Reader hardware. These ports are used only on the original GBA (where the large e-Reader cartridge would be covering the GBA’s link socket). When trying to insert the e-Reader into an original NDS (or GBA-Micro), then the e-Reader’s link plug will hit against the case of the NDS, so it works only with some minor modification to the hardware. There’s no such problem with GBA-SP and NDS-Lite.
There are 3 different e-Reader’s: Japanese/Original, Japanese/Plus, and Non-Japanese. The Original version has only 64K FLASH, no Link Port, and reportedly supports only Z80 code, but no NES/GBA code. The Plus and Non-Japanese versions should be almost identical, except that they reject cards from the wrong region, and that the title strings aren’t ASCII in Japan, the Plus version should be backwards compatible to the Original one.
Nintendo’s current programmers are definetly unable to squeeze a Pac-Man style game into less than 4MBytes. Their solution has been: MORE memory. That is, they’ve put a whopping 8MByte BIOS ROM into the e-Reader, which contains the User Interface, and software emulation for running some of their 20 years old 8bit NES and Game&Watch titles, which do fit on a few dotcode strips.
0 Output to PGA.Pin93 (which seems to be not connected to anything)
1-3 Unknown, read/write-able (not used by e-Reader BIOS)
4-15 Always zero (0)
0 Always zero (0)
1 Reset Something? (0=Normal, 1=Reset)
2 Unknown, always set (1)
3 Unknown, read/write-able (not used by e-Reader BIOS)
4-7 Always zero (0)
8 Unknown, read/write-able (not used by e-Reader BIOS)
9-15 Always zero (0)
Scanline data (40 bytes, for 320 pixels, 1bit per pixel, 0=black, 1=white).
The first (leftmost) pixel is located in the LSB of the LAST byte.
Port E00FFB1h.Bit1 (and [4000202h].Bit13) indicates when a new scanline is present, the data should be then transferred to RAM via DMA3 (SAD=DFC0000h, DAD=buf+y*28h, CNT=80000014h; a slower non-DMA transfer method would result in missed scanlines). After the DMA, software must reset E00FFB1h.Bit1.
Note: The scanning resolution is 1000 DPI.
0-6 Max Brightness (00h..7Fh; 00h=All black, 7Fh=One or more white)
7-15 Always zero
Can be used to adjust the Port E00FF80h..E00FFAFh settings.
0-7 Max Darkness (00h..7Fh; 00h=One or more black, 7Fh=All white)
8-15 Always zero
Can be used to adjust the Port E00FF80h..E00FFAFh settings.
The 320x246 pixel camera input is split into 8x6 blocks (40x41 pixels each), with Block00h=Upper-right, Block07h=Upper-left, …, Block27h=Lower-left. The boundary values for the separate blocks are used for 128-grayscale to 2-color conversion, probably done like “IF Pixel>Boundary THEN white ELSE black”.
0-6 Block Intensity Boundaries (0..7Fh; 7Fh=Whole block gets black)
7 Always zero
The default boundary values are stored in FLASH memory, the values are typically ranging from 28h (outer edges) to 34h (center image), that in respect to the light source (the two LEDs are emitting more light to the center region).
0 Serial Data (Low/High)
1 Serial Clock (Low/High)
2 Serial Direction (0=Input, 1=Output)
3 Led/Irq Enable (0=Off, 1=On; Enable LED and Gamepak IRQ)
4 Start Scan (0=Off, 1=Start) (0-to-1 --> Resync line 0)
5 Phi 16MHz Output (0=Off, 1=On; Enable Clock for Camera, and for LED)
6 Power 3V Enable (0=Off, 1=On; Enable 3V Supply for Camera)
7 Not used (always 0) (sometimes 1) (Read only)
0 Not used (always 0)
1 Scanline Flag (1=Scanline Received, 0=Acknowledge)
2-3 Not used (always 0)
4 Strange Bit (0=Normal, 1=Force Resync/Line0 on certain interval?)
5 LED Anode Voltage (0=3.0V, 1=5.1V; requires E00FFB0h.Bit3+5 to be set)
6 Not used (always 0)
7 Input from PGA.Pin22, always high (not used by e-Reader) (Read Only)
Bit1 can be SET by hardware only, software can only RESET that bit, the Gamepak IRQ flag (Port 4000202h.Bit13) becomes set on 0-to-1 transitions.
Selects the LED Kathode=LOW Duration, aka the LED=ON Duration. That does act as pulse width modulated LED brightness selection (the camera seems to react slowly enough to view the light as being dimmed to medium, rather than seeing the actual light ON and OFF states). The PWM timer seems to be clocked at 8MHz. The hardware clips timer values 2000h..FFFFh to max 2000h (=1ms). Additionally, the e-Reader BIOS clips values to max 11B3h. Default setting is found in FLASH calibration data. A value of 0000h disables the LED.
All 16bit values are ordered MSB,LSB. All registers are whole 8bit Read/Write-able, except 00h,57h-5Ah (read only), and 53h-55h (2bit only).
Port Expl. (e-Reader Setting)
00h Maybe Chip ID (12h) (not used by e-Reader BIOS) (Read Only)
01h (05h) ;-Bit0: 1=auto-repeat scanning?
02h (0Eh)
10h-11h Vertical Scroll (calib_data[30h]+7)
12h-13h Horizontal Scroll (0030h)
14h-15h Vertical Size (00F6h=246)
16h-17h Horizontal Size (0140h=320)
20h-21h H-Blank Duration (00C4h)
22h-23h (0400h) ;-Upper-Blanking in dot-clock units?
25h (var) ;-bit1: 0=enable [57h..5Ah] ?
26h (var) ;\maybe a 16bit value
27h (var) ;/
28h (00h)
30h Brightness/contrast (calib_data[31h]+/-nn)
31h-33h (014h,014h,014h)
34h Brightness/contrast (02h)
50h-52h 8bit Read/Write (not used by e-Reader BIOS)
53h-55h 2bit Read/Write (not used by e-Reader BIOS)
56h 8bit Read/Write (not used by e-Reader BIOS)
57h-58h 16bit value, used to autodetect/adjust register[30h] (Read Only)
59h-5Ah 16bit value, used to autodetect/adjust register[30h] (Read Only)
80h-FFh Mirrors of 00h..7Fh (not used by e-Reader BIOS)
All other ports are unused, writes to those ports are ignored, and reads are returning data mirrored from other ports; that is typically data from 2 or more ports, ORed together.
All 16bit values are using more conventional LSB,MSB ordering, and port numbers are arranged in a more reasonable way. The e-Reader BIOS doesn’t support (or doesn’t require) brightness adjustment for this camera module.
Port Expl. (e-Reader Setting)
00h (22h)
01h (50h)
02h-03h Vertical Scroll (calib_data[30h]+28h)
04h-05h Horizontal Scroll (001Eh)
06h-07h Vertical Size (00F6h) ;=246
08h-09h Horizontal Size (0140h) ;=320
0Ah-0Ch (not used by e-Reader BIOS)
0Dh (01h)
0Eh-0Fh (01EAh) ;=245*2
10h-11h (00F5h) ;=245
12h-13h (20h,F0h) ;maybe min/max values?
14h-15h (31h,C0h) ;maybe min/max values?
16h (00h)
17h-18h (77h,77h)
19h-1Ch (30h,30h,30h,30h)
1Dh-20h (80h,80h,80h,80h)
21h-FFh (not used by e-Reader BIOS)
This appears to be a Micron (aka Aptina) camera (resembling the DSi cameras).
My own e-Reader uses a Type 1 camera module. Not sure if Nintendo has ever manufactured any e-Readers with Type 2 cameras?
E00D000 14h ID String ('Card-E Reader 2001',0,0)
E00D014 2 Sector Checksum (NOT(x+x/10000h); x=sum of all other halfwords)
Begin of actual data (40h bytes)
E00D016 8x6 [00h] Intensity Boundaries for 8x6 blocks ;see E00FF80h..AFh
E00D046 1 [30h] Vertical scroll (0..36h) ;see type1.reg10h/type2.reg02h
E00D047 1 [31h] Brightness or contrast ;see type1.reg30h
E00D048 2 [32h] LED Duration ;see E00FFB2h..B3h
E00D04A 2 [34h] Not used? (0000h)
E00D04C 2 [36h] Signed value, related to adjusting the 8x6 blocks
E00D04E 4 [38h] Not used? (00000077h)
E00D052 4 [3Ch] Camera Type (0=none,1=DV488800,2=Whatever?)
Remaining bytes in this Sector…
E00D056 FAAh Not used (zerofilled) (included in above checksum)
ereader_scan_camera:
call ereader_power_on
call ereader_initialize
for z=1 to number_of_frames
for y=0 to 245
Wait until E00FFB1h.Bit1 gets set by hardware (can be handled by IRQ)
Copy 14h halfwords from DFC0000h to buf+y*28h via DMA3
Reset E00FFB1h.Bit1 by software
next y
;(could now check DFC0028h..DFC0086h/DFC0088h for adjusting E00FF00h..2Fh)
;(could now show image on screen, that may require to stop/pause scanning)
next z
call ereader_power_off
Ret
ereader_power_on:
[4000204h]=5803h ;Init waitstates, and enable Phi 16MHz
[DFA0000h].Bit1=1
Wait(10ms)
[E00FFB0h]=40h ;Enable Power3V and reset other bits
[DFA0000h].Bit1=0
[E00FFB1h]=20h ;Enable Power5V and reset other bits
Wait(40ms)
[E00FFB1h].Bit4=0 ;...should be already 0 ?
[E00FFB0h]=40h+27h ;Phi16MHz=On, SioDtaClkDir=HighHighOut
Ret
ereader_power_off:
[E00FFB0h]=04h ;Power3V=Off, Disable Everything, SioDtaClkDir=LowLowOut
[DFA0000h].Bit1=0 ;...should be already 0
[E00FFB1h].Bit5=0 ;Power5V=Off
Ret
ereader_initialize:
IF calib_data[3Ch] AND 03h = 1 THEN init_camera_type1
[E00FFB0h].Bit4=1 ;ScanStart
IF calib_data[3Ch] AND 03h = 2 THEN init_camera_type2
Copy calib_data[00h..2Fh] to [E00FF80h+00h..2Fh] ;Intensity Boundaries
Copy calib_data[32h..33h] to [E00FFB2h+00h..01h] ;LED Duration LSB,MSB
[E00FFB0h].Bit3=1 ;LedIrqOn
Ret
init_camera_type1:
x=MIN(0,calib_data[31h]-0Bh)
Set Sio Registers (as shown for Camera Type 1, except below values...)
Set Sio Registers [30h]=x [25h]=04h, [26h]=58h, [27h]=6Ch
;(could now detect/adjust <x> based on Sio Registers [57h..5Ah])
Set Sio Registers [30h]=x [25h]=06h, [26h]=E8h, [27h]=6Ch
Ret
init_camera_type2:
Begin Write(A) Write(B) Read(C) Read(D) End Idle PwrOff
Dir ooooooo ooooooo ooooooo iiiiiii iiiiiii ooooooo ooooooo ooooooo
Dta ---____ AAAAAAA BBBBBBB xxxxxCx xxxxxDx ______- ------- _______
Clk ------_ ___---_ ___---_ ___---_ ___---_ ___---- ------- _______
Caution: Accessing the SIO registers appears highly unstable, and seems to require error handling with retries. Not sure what is causing that problem, possibly the registers cannot be accessed during camera-data-scans…?
The e-Reader BIOS uses WAITCNT [4000204h]=5803h when accessing the PGA, that is, gamepak 16.78MHz phi output (bit11-12=3), 8 waits for SRAM region (bit0-1=3), gamepak prefetch enabled (bit14=1), also sets WS0 to 4,2 waits (bit2-4=0), and sets WS2 to odd 4,8 waits (bit8-10=0). The WS2 (probably WS0 too) settings are nonsense, and should work with faster timings (the e-Reader can be accessed in NDS mode, which doesn’t support that slow timings).
C000000h-C7FFFFFh ROM (8MB)
C800000h-DF7FFFFh Open Bus
DF80000h-DF80001h Useless Register (R/W)
DF80002h-DF9FFFFh Mirrors of DF80000h-DF80001h
DFA0000h-DFA0001h Reset Register (R/W)
DFA0002h-DFBFFFFh Mirrors of DFA0000h-DFA0001h
DFC0000h-DFC0027h Scanline Data (320 Pixels) (R)
DFC0028h-DFC0087h Brightest Pixels of 8x6 Blocks (R)
DFC0088h Darkest Pixel of whole Image (R)
DFC0089h-DFC00FFh Always zero
DFC0100h-DFDFFFFh Mirrors of DFC0000h-DFC00FFh
DFE0000h-DFFFFFFh Open Bus
E000000h-E00CFFFh FLASH Bank 0 - Data
E00D000h-E00DFFFh FLASH Bank 0 - Calibration Data
E00E000h-E00EFFFh FLASH Bank 0 - Copy of Calibration Data
E00F000h-E00FF7Fh FLASH Bank 0 - Unused region
E000000h-E00EFFFh FLASH Bank 1 - Data
E00F000h-E00FF7Fh FLASH Bank 1 - Unused region
E00FF80h-E00FFAFh Intensity Boundaries for 8x6 Blocks (R/W)
E00FFB0h Control Register 0 (R/W)
E00FFB1h Control Register 1 (R/W)
E00FFB2h-E00FFB3h LED Duration (16bit) (R/W)
E00FFB4h-E00FFBFh Always zero
E00FFC0h-E00FFFFh Mirror of E00FF80h-E00FFBFh
Mind that WS2 should be accessed by LDRH/STRH, and SRAM region by LDRB/STRB.
Additionally about 32 serial bus registers are contained in the camera module.
The Type 1 initial setting on power-on is 402x302 pixels, the e-Reader uses only 320x246 pixels. The full vertical resolution could be probably used without problems. Port DFC0000h-DFC0027h are restricted to 320 pixels, so larger horizontal resolutions could be probably obtained only by changing the horizontal scroll offset on each 2nd scan.
The camera output is 128 grayscales (via parallel 7bit databus), but the PGA converts it to 2 colors (1bit depth). For still images, it might be possible to get 4 grayshades via 3 scans with different block intensity boundary settings.
No idea if the camera supports serial commands other than 22h and 23h. Namely, it <would> be a quite obvious and basic feature to allow to receive the bitmap via the 2-wire serial bus (alternately to the 7bit databus), if supported, it’d allow to get 7bit images, bypassing 1bit PGA conversion.
When used as actual camera (by cutting an opening in the case), the main problem is the 1bit color depth, which allows only black and white schemes, when/if solving that problem, focusing might be also a problem.
Either the camera or the PGA seem to have a problem on white-to-black transitions in vertical direction, the upper some black pixels are sorts of getting striped or dithered. For example, scanning the large sync marks appears as:
Actual Shape Scanned Shape
XXXXX X X
XXXXXXX X X X
XXXXXXXXX X X X XX
XXXXXXXXX X X X XX
XXXXXXX XXXXXXX
XXXXX XXXXX
That appears only on large black shapes (the smaller data dots look better). Probably the image is scanned from bottom upwards (and the camera senses only the initial transition at the bottom, and then looses track of what it is doing).
Resolution is 342.39 DPI (almost 10 blocks per inch).
Resolution is 134.8 dots/cm (almost 4 blocks per centimeter).
The width and height of each block, and the spacing to the bottom edge of the card is ca. 1/10 inch, or ca. 4 millimeters.
XXX BLOCK 1 XXX BLOCK 2 XXX
XXXXX XXXXX XXXXX
XXXXX X X X X X X X X X X X X XXXXX X X X X X X X X X X X X XXXXX
XXXXX XXXXX XXXXX
XXX HHHHHHHHHHHHHHHHHHHH...... XXX HHHHHHHHHHHHHHHHHHHH...... XXX
.......................... ..........................
...... 3 short lines ..... ..........................
A..................................A..................................A..
A.... 26 long lines ....A........ X = Sync Marks ........A..
A.... (each 34 data dots) ....A........ H = Block Header ........A..
A....(not all lines shown here)....A........ . = Data Bits ........A..
A..................................A........ A = Address Bits ........A..
...... 3 short lines ..... ..........................
...(each 26 data dots).... ..........................
XXX .......................... XXX .......................... XXX
XXXXX XXXXX XXXXX
XXXXX X X X X X X X X X X X X XXXXX X X X X X X X X X X X X XXXXX
XXXXX XXXXX XXXXX
XXX XXX XXX
<ca. 35 blank lines>
___Snip____________________________________________________________________
Each Column consists of 26 dots. From top to bottom: 1 black dot, 8 blank dots, 16 address dots (MSB topmost), and 1 blank dot. The 16bit address values can be calculated as:
addr[0] = 03FFh
for i = 1 to 53
addr[i] = addr[i-1] xor ((i and (-i)) * 769h)
if (i and 07h)=0 then addr[i] = addr[i] xor (769h)
if (i and 0Fh)=0 then addr[i] = addr[i] xor (769h*2)
if (i and 1Fh)=0 then addr[i] = addr[i] xor (769h*4) xor (769h)
next i
Short strips use addr[1..19], long strips use addr[25..53], left to right.
The 18h-byte Block Header is taken from the 1st two bytes (20 dots) of the 1st 0Ch blocks (and is then repeated in the 1st two bytes of further blocks).
00h Unknown (00h)
01h Dotcode type (02h=Short, 03h=Long)
02h Unknown (00h)
03h Address of 1st Block (01h=Short, 19h=Long)
04h Total Fragment Size (40h) ;64 bytes per fragment, of which,
;48 bytes are actual data, the remaining
05h Error-Info Size (10h) ;16 bytes are error-info
06h Unknown (00h)
07h Interleave Value (1Ch=Short, 2Ch=Long)
08h..17h 16 bytes Reed-solomon error correction info for Block Header
In the Block Header (HHHHH), and Data Region (…..), each 4bit are expanded to 5bit, so one byte occupies 10 dots, and each block (1040 data dots) contains 104 bytes.
4bit 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh
5bit 00h 01h 02h 12h 04h 05h 06h 16h 08h 09h 0Ah 14h 0Ch 0Dh 11h 10h
That formatting ensures that there are no more than two continous black dots (in horizontal direction), neither inside of a 5bit value, nor between two 5bit values, however, the address bars are violating that rule, and up to 5 continous black dots can appear at the (..A..) block boundaries.
Data starts with the upper bit of the 5bit value for the upper 4bit of the first byte, which is located at the leftmost dot of the upper line of the leftmost block, it does then extend towards rightmost dot of that block, and does then continue in the next line, until reaching the bottom of the block, and does then continue in the next block. The 1st two bytes of each block contain a portion of the Block Header, the remaining 102 bytes in each block contain data.
A long strip consists of 28 blocks (28*104 = 2912 bytes), a short strip of 18 blocks (18*104 = 1872 bytes). Of which, less than 75% can be actually used for program code, the remaining data contains error correction info, and various headers. See Data Format for more info.
The Interleave Value (I) specifies the number of fragments, and does also specify the step to the next byte inside of a fragment; except that, at the block boundaries (every 104 bytes), the step is 2 bigger (for skipping the next two Block Header bytes).
RAW Offset Content
000h..001h 1st 2 bytes of RAW Header
002h 1st byte of 1st fragment
003h 1st byte of 2nd fragment
... ...
002h+I-1 1st byte of last fragment
002h+I 2nd byte of 1st fragment
003h+I 2nd byte of 2nd fragment
... ...
002h+I*2-1 2nd byte of last fragment
... ...
Each fragment consists of 48 actual data bytes, followed by 16 error correction bytes, followed by 0..2 unused bytes (since I*40h doesn’t exactly match num_blocks*102).
The size of the data region is I*48 bytes (I=Interleave Value, see Dotcode Format), the first 48-byte fragment contains the Data Header, the remaining (I-1) fragments are Data Fragments (which contain title(s), and VPK compressed program code).
Data Header (48 bytes)
Main-Title (17 bytes, or 33 bytes)
Sub-Title(s) (3+18 bytes, or 33 bytes) (for each strip) (optional)
VPK Size (2 byte value, total length of VPK Data in ALL strips)
NULL Value (4 bytes, contained ONLY in 1st strip of GBA strips)
VPK Data (length as defined in VPK Size entry, see above)
Data Header (48 bytes)
Main-Title (17 bytes, or 33 bytes)
Sub-Title(s) (3+18 bytes, or 33 bytes) (for each strip) (optional)
VPK Data (continued from previous strip)
00h-01h Fixed (00h,30h)
02h Fixed (01h) ;01h="Do not calculate Global Checksum" ?
03h Primary Type (see below)
04h-05h Fixed (00h,01h) (don't care)
06h-07h Strip Size (0510h=Short, 0810h=Long Strip) ((I-1)*30h) (MSB,LSB)
08h-0Bh Fixed (00h,00h,10h,12h)
0Ch-0Dh Region/Type (see below)
0Eh Strip Type (02h=Short Strip, 01h=Long Strip) (don't care)
0Fh Fixed (00h) (don't care)
10h-11h Unknown (whatever) (don't care)
12h Fixed (10h) ;10h="Do calculate Data Checksum" ?
13h-14h Data Checksum (see below) (MSB,LSB)
15h-19h Fixed (19h,00h,00h,00h,08h)
1Ah-21h ID String ('NINTENDO')
22h-25h Fixed (00h,22h,00h,09h)
26h-29h Size Info (see below)
2Ah-2Dh Flags (see below)
2Eh Header Checksum (entries [0Ch-0Dh,10h-11h,26h-2Dh] XORed together)
2Fh Global Checksum (see below)
Primary Type [03h] is 8bit,
0 Card Type (upper bit) (see below)
1 Unknown (usually opposite of Bit0) (don't care)
2-7 Unknown (usually zero)
Region/Type [0Ch..0Dh] is 16bit,
0-3 Unknown (don't care)
4-7 Card Type (lower bits) (see below)
8-11 Region/Version (0=Japan/Original, 1=Non-japan, 2=Japan/Plus)
12-15 Unknown (don't care)
Size Info [26h-29h] is 32bit,
0 Unknown (don't care)
1-4 Strip Number (01h..Number of strips)
5-8 Number of Strips (01h..0Ch) (01h..08h for Japan/Original version)
9-23 Size of all Strips (excluding Headers and Main/Sub-Titles)
(same as "VPK Size", but also including the 2-byte "VPK Size" value,
plus the 4-byte NULL value; if it is present)
24-31 Fixed (02h) (don't care)
Flags [2Ah-2Dh] is 32bit,
0 Permission to save (0=Start Immediately, 1=Prompt for FLASH Saving)
1 Sub-Title Flag (0=Yes, 1=None) (Japan/Original: always 0=Yes)
2 Application Type (0=GBA/Z80, 1=NES) (Japan/Original: always 0=Z80)
3-31 Zero (0) (don't care)
Data Checksum [13h-14h] is the complement (NOT) of the sum of all halfwords in all Data Fragments, however, it’s all done in reversed byte order: checksum is calculated with halfwords that are read in MSB,LSB order, and the resulting checksum is stored in MSB,LSB order in the Header Fragment.
Global Checksum [2Fh] is the complement (NOT) of the sum of the first 2Fh bytes in the Data Header plus the sum of all Data Fragment checksums; the Data Fragment checksums are all 30h bytes in a fragment XORed with each other.
Titles can be 33 bytes for both Main and Sub (Format 0Eh), or Main=17 bytes and Sub=3+18 bytes (Formats 02h..05h). In the 3+N bytes form, the first 3 bytes (24bit) are are used to display “stats” information in form of “HP: h1 ID: i1-i2-i3”, defined as:
Bit Expl.
0-3 h1, values 1..15 shown as "10..150", value 0 is not displayed
4-6 i3, values 0..7 shown as "A..G,#"
7-13 i2, values 0..98 shown as "01..99" values 99..127 as "A0..C8"
14-18 i1, values 0..31 shown as "A..Z,-,_,{HP},.,{ID?},:"
19-22 Unknown
23 Disable stats (0=Show as "HP: h1 ID: i1-i2-i3", 1=Don't show it)
The N bytes portion contains the actual title, which must be terminated by 00h (so the max length is N-1 characters, if it is shorter than N-1, then the unused bytes are padded by further 00h’s). The character set is normal ASCII for non-Japan (see Region/Version entry in header), and 2-byte SHIFT-JIS for Japanese long-titles (=max 16 2-byte chars) with values as so:
00h --> end-byte
81h,40h --> SPC
81h,43h..97h --> punctuation marks
82h,4Fh..58h --> "0..9"
82h,60h..79h --> "A..Z"
82h,81h..9Ah --> "a..z"
And 1-byte chars for Japanese short-titles,
00 = end-byte
01 = spc
02..0B = 0..9
0C..AF = japanese
B0..B4 = dash, male, female, comma, round-dot
B5..C0 = !"%&~?/+-:.'
C1..DA = A..Z
DB..DF = unused (blank)
E0..E5 = japanese
E6..FF = a..z
N/A = #$()*;<=>@[\]^_`{|}
Additionally to the Main-Title, optional Sub-Titles for each strip can be included (see Sub-Title Flag in header). If enabled, then ALL strip titles are included in each strip (allowing to show a preview of which strips have/haven’t been scanned yet).
The e-Reader can display maximum of 8 sub-titles, if the data consists of more than 8 strips, then sub-titles aren’t displayed (so it’d be waste of space to include them in the dotcodes).
The Main Title gets clipped to 128 pixels width (that are, circa 22 characters), and, the e-Reader BIOS acts confused on multi-strip games with Main Titles longer than 26 characters (so the full 33 bytes may be used only in Japan; with 16bit charset).
If the title is empty (00h-filled), and there is only one card in the application, then the application is started immediately. That, without allowing the user to save it in FLASH memory.
Caution: Although shorter Titles do save memory, they do act unpleasant: the text “(C) P-Letter” will be displayed at the bottom of the loading screen.
On Japanese/Original, 8bit sub-titles can be up to 18 characters (without any end-byte) (or less when stats are enabled, due to limited screen width).
00h..01h Blank Screen (?)
02h..03h Dotcode Application with 17byte-title, with stats, load music A
04h..05h Dotcode Application with 17byte-title, with stats, load music B
06h..07h P-Letter Attacks
08h..09h Construction Escape
0Ah..0Bh Construction Action
0Ch..0Dh Construction Melody Box
0Eh Dotcode Application with 33byte-title, without stats, load music A
0Fh Game specific cards
10h..1Dh P-Letter Viewer
1Eh..1Fh Same as 0Eh and 0Fh (see above)
The ‘Application’ types are meant to be executable GBA/Z80/NES programs.
The GBA/Z80/NES program code is stored in the VPK compressed area.
NES-type is indicated by header [2Ah].Bit2, GBA-type is indicated by the NULL value inserted between VPK Size and VPK Data, otherwise Z80-type is used.
Load Address and Entrypoint are at 2000000h (in ARM state). The 32bit word at 2000008h is eventually destroyed by the e-Reader. Namely,
IF e-Reader is Non-Japanese,
AND [2000008h] is outside of range of 2000000h..20000E3h,
AND only if booted from camera (not when booted from FLASH?),
THEN [2000008h]=[2000008h]-0001610Ch ELSE [2000008h] kept intact
Existing multiboot-able GBA binaries can be converted to e-Reader format by,
Store "B 20000C0h" at 2000000h ;redirect to RAM-entrypoint
Zerofill 2000004h..20000BFh ;erase header (for better compression rate)
Store 01h,01h at 20000C4h ;indicate RAM boot
The GBA code has full access to the GBA hardware, and may additionally use whatever API functions contained in the e-Reader BIOS. With the incoming LR register value, “mov r0,N, bx lr” returns to the e-Reader BIOS (with N being 0=Restart, or 2=To_Menu). No idea if it’s necessary to preserve portions of RAM when returning to the e-Reader BIOS?
Caution: Unlike for normal GBA cartridges/multiboot files, the hardware is left uninitialized when booting dotcodes (among others: sound DMA is active, and brightness is set to zero), use “mov r0,0feh, swi 010000h” to get the normal settings.
Emulates a NES (Nintendo Entertainment System) console (aka Family Computer).
The visible 240x224 pixel NES/NTSC screen resolution is resampled to 240x160 to match the smaller vertical resolution of the GBA hardware. So, writing e-Reader games in NES format will result in blurred screen output. The screen/sound/joypad is accessed via emulated NES I/O ports, program code is running on an emulated 6502 8bit CPU, for more info on the NES hardware, see no$nes debugger specifications, or
The e-Reader’s NES emulator supports only 16K PRG ROM, followed by 8K VROM. The emulation accuracy is very low, barely working with some of Nintendo’s own NES titles; running the no$nes diagnostics program on it has successfully failed on ALL hardware tests ;-)
The load address for the 16K PRG-ROM is C000h, the 16bit NMI vector at [FFFAh] is encrypted like so:
for i=17h to 0
for j=07h to 0, nmi = nmi shr 1, if carry then nmi = nmi xor 8646h, next j
nmi = nmi xor (byte[dmca_data+i] shl 8)
next i
dmca_data: db 0,0,'DMCA NINTENDO E-READER'
The 16bit reset vector at [FFFCh] contains:
Bit0-14 Lower bits of Entrypoint (0..7FFFh = Address 8000h..FFFFh)
Bit15 Nametable Mode (0=Vertical Mirroring, 1=Horizontal Mirroring)
reportedly,
(NES limitations, 1 16K program rom + 1-2 8K CHR rom, mapper 0 and 1)
ines mapper 1 would be MMC1, rather than CNROM (ines mapper 3)?
but, there are more or less NONE games that have 16K PRG ROM + 16K VROM?
The L+R Button key-combination allows to reset the NES, however, there seems to be no way to return to the e-Reader BIOS.
The e-Reader doesn’t support the following Z80 opcodes:
CB [Prefix] E0 RET PO E2 JP PO,nn E4 CALL PO,nn 27 DAA 76 HALT
ED [Prefix] E8 RET PE EA JP PE,nn EC CALL PE,nn D3 OUT (n),A
DD [IX Prefix] F3 DI 08 EX AF,AF' F4 CALL P,nn DB IN A,(n)
FD [IY Prefix] FB EI D9 EXX FC CALL M,nn xx RST 00h..38h
That is leaving not more than six supported Z80 opcodes (DJNZ, JR, JR c/nc/z/nz), everything else are 8080 opcodes. Custom opcodes are:
76 WAIT A frames, D3 WAIT n frames, and C7/CF RST 0/8 used for API calls.
The load address and entrypoint are at 0100h in the emulated Z80 address space. The Z80 doesn’t have direct access to the GBA hardware, instead video/sound/joypad are accessed via API functions, invoked via RST 0 and RST 8 opcodes, followed by an 8bit data byte, and with parameters in the Z80 CPU registers. For example, “ld a,02h, rst 8, db 00h” does return to the e-Reader BIOS.
The Z80/8080 emulation is incredibly inefficient, written in HLL code, developed by somebody whom knew nothing about emulation nor about ARM nor about Z80/8080 processors.
Original e-Reader supports Z80 code only, but can be tweaked to run GBA-code:
retry:
ld bc,data // ld hl,00c8h ;src/dst
lop:
ld a,[bc] // inc bc // ld e,a ;lsb
ld a,[bc] // inc bc // ld d,a ;msb
dw 0bcfh ;aka rst 8 // db 0bh ;[4000000h+hl]=de (DMA registers)
inc hl // inc hl // ld a,l
cp a,0dch // jr nz,lop
mod1 equ $+1
dw 37cfh ;aka rst 8 // db 37h ;bx 3E700F0h
;below executed only on jap/plus... on jap/plus, above 37cfh is hl=[400010Ch]
ld a,3Ah // ld [mod1],a ;bx 3E700F0h (3Ah instead 37h)
ld hl,1 // ld [mod2],hl // ld [mod3],hl ;base (0200010Ch instead 0201610Ch)
jr retry
data:
mod2 equ $+1
dd loader ;40000C8h dma2sad (loader) ;\
dd 030000F0h ;40000CCh dma2dad (mirrored 3E700F0h) ; relocate loader
dd 8000000ah ;40000D0h dma2cnt (copy 0Ah x 16bit) ;/
mod3 equ $+1
dd main ;40000D4h dma3sad (main) ;\prepare main reloc
dd 02000000h ;40000D8h dma3dad (2000000h) ;/dma3cnt see loader
.align 2 ;alignment for 16bit-halfword
org $+201600ch ;jap/plus: adjusted to org $+200000ch
loader:
mov r0,80000000h ;(dma3cnt, copy 10000h x 16bit)
mov r1,04000000h ;i/o base
strb r1,[r1,208h] ;ime=0 (better disable ime before moving ram)
str r0,[r1,0DCh] ;dma3cnt (relocate to 2000000h)
mov r15,2000000h ;start relocated code at 2000000h in ARM state
main:
;...insert/append whatever ARM code here...
end
db 76h ;Wait8bit A
db D3h,xxh ;Wait8bit xxh
db C7h,xxh ;RST0_xxh
db CFh,xxh ;RST8_xxh
ld r,[00xxh] ;get system values (addresses differ on jap/ori)
ld r,[00C2h..C3h] ;GetKeyStateSticky (jap/ori: 9F02h..9F03h)
ld r,[00C4h..C5h] ;GetKeyStateRaw (jap/ori: 9F04h..9F05h)
ld r,[00C0h..C1h] ;see Exit and ExitRestart
ld r,[00D0h..D3h] ;see Mul16bit
For jap/ori, 9Fxxh isn’t forwards compatible with jap/plus, so it’d be better to check joypad via IoRead.
bx [30075FCh] ;ApiVector ;in: r0=func_no,r1,r2,r3,[sp+0],[sp+4],[sp+8]=params
bx lr ;Exit ;in: r0 (0=Restart, 2=To_Menu)
The various Wait opcodes and functions are waiting as many frames as specified. Many API functions have no effect until the next Wait occurs.
RST0_00h FadeIn, A speed, number of frames (0..x)
RST0_01h FadeOut
RST0_02h BlinkWhite
RST0_03h (?)
RST0_04h (?) blend_func_unk1
RST0_05h (?)
RST0_06h (?)
RST0_07h (?)
RST0_08h (?)
RST0_09h (?) _020264CC_check
RST0_0Ah (?) _020264CC_free
RST0_0Bh N/A (bx 0)
RST0_0Ch N/A (bx 0)
RST0_0Dh N/A (bx 0)
RST0_0Eh N/A (bx 0)
RST0_0Fh N/A (bx 0)
RST0_10h LoadSystemBackground, A number of background (1..101), E bg# (0..3)
RST0_11h SetBackgroundOffset, A=bg# (0..3), DE=X, BC=Y
RST0_12h SetBackgroundAutoScroll
RST0_13h SetBackgroundMirrorToggle
RST0_14h (?)
RST0_15h (?)
RST0_16h (?) write_000000FF_to_02029494_
RST0_17h (?)
RST0_18h (?)
RST0_19h SetBackgroundMode, A=mode (0..2)
RST0_1Ah (?)
RST0_1Bh (?)
RST0_1Ch (?)
RST0_1Dh (?)
RST0_1Eh (?)
RST0_1Fh (?)
RST0_20h LayerShow
RST0_21h LayerHide
RST0_22h (?)
RST0_23h (?)
RST0_24h ... [20264DCh+A*20h+1Ah]=DE, [20264DCh+A*20h+1Ch]=BC
RST0_25h (?)
RST0_26h (?)
RST0_27h (?)
RST0_28h (?)
RST0_29h (?)
RST0_2Ah (?)
RST0_2Bh (?)
RST0_2Ch (?)
RST0_2Dh LoadCustomBackground, A bg# (0..3), DE pointer to struct_background,
max. tile data size = 3000h bytes, max. map data size = 1000h bytes
RST0_2Eh GBA: N/A - Z80: (?)
RST0_2Fh (?)
RST0_30h CreateSystemSprite, - - (what "- -" ???)
RST0_31h SpriteFree, HL sprite handle
RST0_32h SetSpritePos, HL=sprite handle, DE=X, BC=Y
RST0_33h (?) sprite_unk2
RST0_34h SpriteFrameNext
RST0_35h SpriteFramePrev
RST0_36h SetSpriteFrame, HL=sprite handle, E=frame number (0..x)
RST0_37h (?) sprite_unk3
RST0_38h (?) sprite_unk4
RST0_39h SetSpriteAutoMove, HL=sprite handle, DE=X, BC=Y
RST0_3Ah (?) sprite_unk5
RST0_3Bh (?) sprite_unk6
RST0_3Ch SpriteAutoAnimate
RST0_3Dh (?) sprite_unk7
RST0_3Eh SpriteAutoRotateUntilAngle
RST0_3Fh SpriteAutoRotateByAngle
RST0_40h SpriteAutoRotateByTime
RST0_41h (?) sprite_unk8
RST0_42h SetSpriteAutoMoveHorizontal
RST0_43h SetSpriteAutoMoveVertical
RST0_44h (?) sprite_unk9
RST0_45h SpriteDrawOnBackground
RST0_46h SpriteShow, HL=sprite handle
RST0_47h SpriteHide, HL=sprite handle
RST0_48h SpriteMirrorToggle
RST0_49h (?) sprite_unk10
RST0_4Ah (?) sprite_unk11
RST0_4Bh (?) sprite_unk12
RST0_4Ch GetSpritePos
RST0_4Dh CreateCustomSprite
RST0_4Eh (?)
RST0_4Fh (?) sprite_unk14
RST0_50h (?) sprite_unk15
RST0_51h (?) sprite_unk16
RST0_52h (?) sprite_unk17
RST0_53h (?) sprite_unk18
RST0_54h (?)
RST0_55h (?) sprite_unk20
RST0_56h (?)
RST0_57h SpriteMove
RST0_58h (?) sprite_unk22
RST0_59h (?) sprite_unk23
RST0_5Ah (?) sprite_unk24
RST0_5Bh SpriteAutoScaleUntilSize, C=speed (higher value is slower),
HL=sprite handle, DE=size (0100h = normal size,
lower value = larger, higher value = smaller)
RST0_5Ch SpriteAutoScaleBySize
RST0_5Dh SpriteAutoScaleWidthUntilSize
RST0_5Eh SpriteAutoScaleHeightBySize
RST0_5Fh (?)
RST0_60h (?)
RST0_61h (?)
RST0_62h (?)
RST0_63h (?)
RST0_64h hl=[[2024D28h+a*4]+12h]
RST0_65h (?) sprite_unk25
RST0_66h SetSpriteVisible, HL=sprite handle, E=(0=not visible, 1=visible)
RST0_67h (?) sprite_unk26
RST0_68h (?) set_sprite_unk27
RST0_69h (?) get_sprite_unk27
RST0_6Ah (?)
RST0_6Bh (?)
RST0_6Ch (?)
RST0_6Dh (?)
RST0_6Eh hl=[hl+000Ah] ;r0=[r1+0Ah]
RST0_6Fh (?)
RST0_70h (?)
RST0_71h (?)
RST0_72h (?)
RST0_73h (?)
RST0_74h (?)
RST0_75h (?)
RST0_76h (?)
RST0_77h (?)
RST0_78h (?)
RST0_79h (?)
RST0_7Ah (?)
RST0_7Bh (?)
RST0_7Ch (?) _0202FD2C_unk12
RST0_7Dh Wait16bit ;HL=num_frames (16bit variant of Wait8bit opcode/function)
RST0_7Eh SetBackgroundPalette, HL=src_addr, DE=offset, C=num_colors (1..x)
RST0_7Fh GetBackgroundPalette(a,b,c)
RST0_80h SetSpritePalette, HL=src_addr, DE=offset, C=num_colors (1..x)
RST0_81h GetSpritePalette(a,b,c)
RST0_82h ClearPalette
RST0_83h (?) _0202FD2C_unk11
RST0_84h (?)
RST0_85h (?)
RST0_86h (?)
RST0_87h (?) _0202FD2C_unk8
RST0_88h (?) _0202FD2C_unk7
RST0_89h (?)
RST0_8Ah (?) _0202FD2C_unk6
RST0_8Bh (?) _0202FD2C_unk5
RST0_8Ch GBA: N/A - Z80: (?)
RST0_8Dh GBA: N/A - Z80: (?)
RST0_8Eh (?)
RST0_8Fh WindowHide
RST0_90h CreateRegion, H=bg# (0..3), L=palbank# (0..15),
D,E,B,C=x1,y1,cx,cy (in tiles), return: n/a (no$note: n/a ???)
RST0_91h SetRegionColor
RST0_92h ClearRegion
RST0_93h SetPixel
RST0_94h GetPixel
RST0_95h DrawLine
RST0_96h DrawRect
RST0_97h (?) _0202FD2C_unk4
RST0_98h SetTextColor, A=region handle, D=color foreground (0..15),
E=color background (0..15)
RST0_99h DrawText, A=region handle, BC=pointer to text, D=X, E=Y
(non-japan uses ASCII text, but japanese e-reader's use STH ELSE?)
RST0_9Ah SetTextSize
RST0_9Bh (?) RegionUnk7
RST0_9Ch (?) _0202FD2C_unk3
RST0_9Dh (?) _0202FD2C_unk2
RST0_9Eh (?) _0202FD2C_unk1
RST0_9Fh Z80: (?) - GBA: SetBackgroundModeRaw
RST0_A0h (?)
RST0_A1h (?)
RST0_A2h (?) RegionUnk6
RST0_A3h GBA: N/A - Z80: (?)
RST0_A4h GBA: N/A - Z80: (?)
RST0_A5h (?)
RST0_A6h (?)
RST0_A7h (?)
RST0_A8h (?)
RST0_A9h (?)
RST0_AAh (?)
RST0_ABh (?)
RST0_ACh (?)
RST0_ADh (?) RegionUnk5
RST0_AEh [202FD2Ch+122h]=A
RST0_AFh [202FD2Ch+123h]=A
RST0_B0h [202FD2Ch+124h]=A
RST0_B1h (?)
RST0_B2h (?)
RST0_B3h GBA: N/A - Z80: Sqrt ;hl=sqrt(hl)
RST0_B4h GBA: N/A - Z80: ArcTan ;hl=ArcTan2(hl,de)
RST0_B5h Sine ;hl=sin(a)*de
RST0_B6h Cosine ;hl=cos(a)*de
RST0_B7h (?)
RST0_B8h (?)
RST0_B9h N/A (bx 0)
RST0_BAh N/A (bx 0)
RST0_BBh N/A (bx 0)
RST0_BCh N/A (bx 0)
RST0_BDh N/A (bx 0)
RST0_BEh N/A (bx 0)
RST0_BFh N/A (bx 0)
Below Non-Japan and Japan/Plus only (not Japan/Ori)
RST0_C0h GetTextWidth(a,b)
RST0_C1h GetTextWidthEx(a,b,c)
RST0_C2h (?)
RST0_C3h Z80: N/A (bx 0) - GBA: (?)
RST0_C4h (?)
RST0_C5h (?)
RST0_C6h (?)
RST0_C7h (?)
RST0_C8h (?)
RST0_C9h (?)
RST0_CAh (?)
RST0_CBh (?)
RST0_CCh (?)
RST0_CDh N/A (bx lr)
RST0_CEh ;same as RST0_3Bh, but with 16bit mask
RST0_CFh ;same as RST0_3Eh, but with 16bit de
RST0_D0h ;same as RST0_3Fh, but with 16bit de
RST0_D1h ;same as RST0_5Bh, but with 16bit de
RST0_D2h ;same as RST0_5Ch, but with 16bit de
RST0_D3h ;same as RST0_5Dh, but with 16bit de
RST0_D4h ;same as RST0_5Eh, but with 16bit de
RST0_D5h (?)
RST0_D6h (?)
RST0_D7h ;[202FD2Ch+125h]=A
RST0_D8h (?)
RST0_D9h (?)
RST0_DAh (?)
RST0_DBh ;A=[3003E51h]
RST0_DCh ;[3004658h]=01h
RST0_DDh DecompressVPKorNonVPK
RST0_DEh FlashWriteSectorSingle(a,b)
RST0_DFh FlashReadSectorSingle(a,b)
RST0_E0h SoftReset
RST0_E1h GetCartridgeHeader ;[hl+0..BFh]=[8000000h..80000BFh]
RST0_E2h GBA: N/A - Z80: bx hl ;in: hl=addr, af,bc,de,sp=param, out: a
RST0_E3h Z80: N/A (bx 0) - GBA: (?)
RST0_E4h (?)
RST0_E5h (?)
RST0_E6h (?)
RST0_E7h (?)
RST0_E8h (?)
RST0_E9h ;[2029498h]=0000h
RST0_EAh Z80: N/A (bx 0) - GBA: InitMemory(a)
RST0_EBh (?) BL_irq_sio_dma3
RST0_ECh ;hl = [3003E30h]*100h + [3003E34h]
RST0_EDh FlashWriteSectorMulti(a,b,c)
RST0_EEh FlashReadPart(a,b,c)
RST0_EFh ;A=((-([2029416h] xor 1)) OR (+([2029416h] xor 1))) SHR 31
RST0_F0h (?) _unk1
RST0_F1h RandomInit ;in: hl=random_seed
RST0_F2h (?)
Below Japan/Plus only
RST0_F3h (?)
RST0_F4h (?)
RST0_F5h (?)
RST0_F6h (?)
RST0_F7h GBA: N/A - Z80: (?)
Below is undefined/garbage (values as so in Z80 mode)
Jap/Ori: RST0_C0h N/A (bx 0)
Jap/Ori: RST0_C1h..FFh Overlaps RST8 jump list
Non-Jap: RST0_F3h..FFh Overlaps RST8 jump list
Jap/Pls: RST0_F8h..FFh Overlaps RST8 jump list
RST8_00h GBA: N/A - Z80: Exit ;[00C0h]=a ;(1=restart, 2=exit)
RST8_01h GBA: N/A - Z80: Mul8bit ;hl=a*e
RST8_02h GBA: N/A - Z80: Mul16bit ;hl=hl*de, s32[00D0h]=hl*de
RST8_03h Div ;hl=hl/de
RST8_04h DivRem ;hl=hl mod de
RST8_05h PlaySystemSound ;in: hl=sound_number
RST8_06h (?) sound_unk1
RST8_07h Random8bit ;a=random(0..FFh)
RST8_08h SetSoundVolume
RST8_09h BcdTime ;[de+0..5]=hhmmss(hl*bc)
RST8_0Ah BcdNumber ;[de+0..4]=BCD(hl), [de+5]=00h
RST8_0Bh IoWrite ;[4000000h+hl]=de
RST8_0Ch IoRead ;de=[4000000h+hl]
RST8_0Dh GBA: N/A - Z80: (?)
RST8_0Eh GBA: N/A - Z80: (?)
RST8_0Fh GBA: N/A - Z80: (?)
RST8_10h GBA: N/A - Z80: (?)
RST8_11h DivSigned ;hl=hl/de, signed
RST8_12h RandomMax ;a=random(0..a-1)
RST8_13h SetSoundSpeed
RST8_14h hl=[202FD20h]=[2024CACh]
RST8_15h hl=[2024CACh]-[202FD20h]
RST8_16h SoundPause
RST8_17h SoundResume
RST8_18h PlaySystemSoundEx
RST8_19h IsSoundPlaying
RST8_1Ah (?)
RST8_1Bh (?)
RST8_1Ch (?)
RST8_1Dh GetExitCount ;a=[2032D34h]
RST8_1Eh Permille ;hl=de*1000/hl
RST8_1Fh GBA: N/A - Z80: ExitRestart;[2032D38h]=a, [00C0h]=0001h ;a=?
RST8_20h GBA: N/A - Z80: WaitJoypad ;wait until joypad<>0, set hl=joypad
RST8_21h GBA: N/A - Z80: (?)
RST8_22h (?) _sound_unk7
RST8_23h (?) _sound_unk8
RST8_24h (?) _sound_unk9
RST8_25h (?) _sound_unk10
RST8_26h Mosaic ;bg<n>cnt.bit6=a.bit<n>, [400004Ch]=de
RST8_27h (?)
RST8_28h (?)
RST8_29h (?)
RST8_2Ah (?) get_8bit_from_2030110h
RST8_2Bh (?)
RST8_2Ch (?) get_16bit_from_2030112h ;jap/ori: hl=[20077B2h]
RST8_2Dh (?) get_16bit_from_2030114h ;jap/ori: hl=[20077B4h]
RST8_2Eh (?)
RST8_2Fh PlayCustomSound(a,b)
Below not for Japanese/Original
(the renumbered functions can be theoretically used on japanese/original)
(but, doing so would blow forwards compatibility with japanese/plus)
RST8_30h (ori: none) GBA: N/A - Z80: (?)
RST8_31h (ori: none) PlayCustomSoundEx(a,b,c)
RST8_32h (ori: RST8_30h) BrightnessHalf ;[4000050h]=00FFh,[4000054h]=0008h
RST8_33h (ori: RST8_31h) BrightnessNormal ;[4000050h]=0000h
RST8_34h (ori: RST8_32h) N/A (bx lr)
RST8_35h (ori: RST8_33h) (?)
RST8_36h (ori: RST8_34h) ResetTimer ;[400010Ch]=00000000h, [400010Eh]=A+80h
RST8_37h (ori: RST8_35h) GetTimer ;hl=[400010Ch]
RST8_38h (ori: none) GBA: N/A - Z80: (?)
Below is undefined/reserved/garbage (values as so in Z80 mode)
(can be used to tweak jap/ori to start GBA-code from inside of Z80-code)
(that, after relocating code to 3000xxxh via DMA via IoWrite function)
RST8_39h (ori: RST8_36h) bx 0140014h
RST8_3Ah (ori: RST8_37h) bx 3E700F0h
RST8_3Bh (ori: RST8_38h) bx 3E70000h+1
RST8_3Ch (ori: RST8_39h) bx 3E703E6h+1
RST8_3Dh (ori: RST8_3Ah) bx 3E703E6h+1
RST8_3Eh (ori: RST8_3Bh) bx 3E703E6h+1
RST8_3Fh (ori: RST8_3Ch) bx 3E703E6h+1
40h-FFh (ori: 3Dh-FFh) bx ...
RSTX_00h Wait8bit ;for 16bit: RST0_7Dh
RSTX_01h GetKeyStateSticky()
RSTX_02h GetKeyStateRaw()
RSTX_03h (?)
RSTX_04h (?)
collected32bit=80000000h ;initially empty (endflag in bit31)
for i=0 to 3, id[i]=read_bits(8), next i, if id[0..3]<>'vpk0' then error
dest_end=dest+read_bits(32) ;size of decompressed data (of all strips)
method=read_bits(8), if method>1 then error
tree_index=0, load_huffman_tree, disproot=tree_index
tree_index=tree_index+1, load_huffman_tree, lenroot=tree_index
;above stuff is contained only in the first strip. below loop starts at
;current location in first strip, and does then continue in further strips.
decompress_loop:
if read_bits(1)=0 then ;copy one uncompressed data byte,
[dest]=read_bits(8), dest=dest+1 ;does work without huffman trees
else
if disproot=-1 or lenroot=-1 then error ;compression does require trees
disp=read_tree(disproot)
if method=1 ;disp*4 is good for 32bit ARM opcodes
if disp>2 then disp=disp*4-8 else disp=disp+4*read_tree(disproot)-7
len=read_tree(lenroot)
if len=0 or disp<=0 or dest+len-1>dest_end then error ;whoops
for j=1 to len, [dest]=[dest-disp], dest=dest+1, next j
if dest<dest_end then decompress_loop
ret
mov data=0
for i=1 to num
shl collected32bit,1 ;move next bit to carry, or set zeroflag if empty
if zeroflag
collected32bit=[src+0]*1000000h+[src+1]*10000h+[src+2]*100h+[src+3]
src=src+4 ;read data in 32bit units, in reversed byte-order
carryflag=1 ;endbit
rcl collected32bit,1 ;move bit31 to carry (and endbit to bit0)
rcl data,1 ;move carry to data
next i
ret(data)
i=root_index
while node[i].right<>-1 ;loop until reaching data node
if read_bits(1)=1 then i=node[i].right else i=node[i].left
i=node[i].left ;get number of bits
i=read_bits(i) ;read that number of bits
ret(i) ;return that value
stacktop=sp
if read_bits(1)=1 then tree_index=-1, ret ;exit (empty)
node[tree_index].right=-1 ;indicate data node
node[tree_index].left=read_bits(8) ;store data value
if read_bits(1)=1 then ret ;exit (only 1 data node at root)
push tree_index ;save previous (child) node
tree_index=tree_index+1
jmp data_injump
load_loop:
push tree_index ;save previous (child) node
tree_index=tree_index+1
if read_bits(1)=1 then parent_node
data_injump:
node[tree_index].right=-1 ;indicate data node
node[tree_index].left=read_bits(8) ;store data value
jmp load_loop
parent_node:
pop node[tree_index].right ;store 1st child
pop node[tree_index].left ;store 2nd child
if sp<>stacktop then jmp load_loop
if read_bits(1)=0 then error ;end bit (must be 1)
ret
The best values for the huffman trees that I’ve found are 6,9,12-bit displacements for method 0 (best for NES/Z80 code), and two less for method 1, ie. 4,7,10-bit (best for GBA code). And 2,4,10-bit for the length values. The smallest value in node 0, and the other values in node 10 and 11.
The decompression works similar to the GBA BIOS’es LZ77 decompression function, but without using fixed bit-widths of length=4bit and displacement=12bit, instead, the bit-widths are read from huffman trees (which can also define fixed bit-widths; if data is located directly in the root node).
Unlike the GBA BIOS’es Huffman decompression function, the trees are starting with data entries, end are ending with the root entry. The above load function deciphers the data, and returns the root index.
With the variable bit-widths, the VPK compression rate is quite good, only, it’s a pity that the length/disp values are zero-based, eg. for 2bit and 4bit lengths, it’d be much better to assign 2bit as 2..5, and 4bit as 6..21.
The e-Reader additionally supports an alternate decompression function, indicated by the absence of the “vpk0” ID, which supports compression of increasing byte-values, which isn’t useful for program code.
Bit15 of the VPK Size value seems to disable (de-)compression, the VPK Data field is then containing plain uncompressed data.
The Error Correction Information that is appended at the end of the Block Header & Data Fragments consists of standard Reed-Solomon codes, which are also used for CD/DVD disks, DSL modems, and digital DVB television signals. That info allows to locate and repair a number of invalid data bytes.
Below code shows how to create and verify error-info (but not how to do the actual error correction). The dtalen,errlen values should be 18h,10h for the Block Header, and 40h,10h for Data Fragments; the latter settings might be possible to get changed to other values though?
reverse_byte_order(data,dtalen)
zerofill_error_bytes(data,errlen)
for i=dtalen-1 to errlen ;loop across data portion
z = rev[ data[i] xor data[errlen-1] ] ;
for j=errlen-1 to 0 ;loop across error-info portion
if j=0 then x=00h else x=data[j-1]
if z<>FFh then
y=gg[j], if y<>FFh then
y=y+z, if y>=FFh then y=y-FFh
x=x xor pow[y]
data[j]=x
next j
next i
invert_error_bytes(data,errlen)
reverse_byte_order(data,dtalen)
reverse_byte_order(data,dtalen)
invert_error_bytes(data,errlen)
make_rev(data,dtalen)
for i=78h to 78h+errlen-1
x=0, z=0
for j=0 to dtalen-1
y=data[j]
if y<>FFh then
y=y+z, if y>=FFh then y=y-FFh
x=x xor pow[y]
z=z+i, if z>=FFh then z=z-FFh
next j
if x<>0 then error
next i
;(if errors occured, could correct them now)
make_pow(data,dtalen)
invert_error_bytes(data,errlen)
reverse_byte_order(data,dtalen)
for i=0 to len-1, data[i]=rev[data[i]], next i
for i=0 to len-1, data[i]=pow[data[i]], next i
for i=0 to len-1, data[i]=data[i] xor FFh, next i
for i=0 to len-1, data[i]=00h, next i
for i=0 to (len-1)/2, x=data[i], data[i]=data[len-i], data[len-i]=x, next i
x=01h, pow[FFh]=00h, rev[00h]=FFh
for i=00h to FEh
pow[i]=x, rev[x]=i, x=x*2, if x>=100h then x=x xor 187h
next i
gg[0]=pow[78h]
for i=1 to errlen-1
gg[i]=01h
for j=i downto 0
if j=0 then y=00h else y=gg[j-1]
x=gg[j], if x<>00h then
x=rev[x]+78h+i, if x>=FFh then x=x-FFh
y=y xor pow[x]
gg[j]=y
next j
next i
make_rev(gg,errlen)
With above value of 78h, and errlen=10h, gg[00h..0Fh] will be always:
00h,4Bh,EBh,D5h,EFh,4Ch,71h,00h,F4h,00h,71h,4Ch,EFh,D5h,EBh,4Bh
So using a hardcoded table should take up less memory than calculating it.
The actual error correction should be able to fix up to “errlen” errors at known locations (eg. data from blocks that haven’t been scanned, or whose 5bit-to-4bit conversion had failed due to an invalid 5bit value), or up to “errlen/2” errors at unknown locations. The corrected data isn’t guaranteed to be correct (even if it looks okay to the “verify” function), so the Data Header checksums should be checked, too.
For more info, I’ve found Reed-Solomon source code from Simon Rockliff, and an updated version from Robert Morelos-Zaragoza and Hari Thirumoorthy to be useful. For getting started with that source, some important relationships & differences are:
pow = alpha_to, but generated as shown above
rev = index_of, dito
b0 = 78h
nn = dtalen
kk = dtalen-errlen
%nn = MOD FFh (for the ereader that isn't MOD dtalen)
-1 = FFh
And, the ereader processes data/errinfo backwards, starting at the last byte.
Contains a picture of the whole dotcode strip with address bars and sync marks (see Dotcode chapter) in Microsoft’s Bitmap format. The image is conventionally surrounded by a blank 2-pixel border, resulting in a size of 989x44 pixels for long strips. The file should should have 1bit color depth. The pixels per meter entry should match the desired printing resolution, either 300 DPI or 360 DPI. But, resolution of printer hardware is typically specified in inch rather than in meters, so an exact match isn’t supported by Microsoft. Most homebrew .BMP files contain nonsense resolutions like 200 DPI, or 300 dots per meter (ca. 8 DPI).
Same as BMP, but should contain a dotcode scanned at 1200 DPI, with correct orientation (the card-edge side at the bottom of the image), and containing only the dotcode (not the whole card), so the JPG size should be about 3450x155 pixels for long strips.
No$gba currently doesn’t work with progressive JPGs. Scans with white background can be saved as monochrome JPG. Scans with red/yellow background should contain a correct RED layer (due to the red LED light source) (the brightness of the green/blue layers can be set to zero for better compression).
Contains the “raw” information from the BMP format, that is, 2-byte block header, 102-byte data, 2-byte block header, 102-byte data, etc. The data portion is interleaved, and includes the full 48-byte data header, titles, vpk compressed data, error-info, and unused bytes. RAW files are excluding Address Bars, Sync Marks, and 4bit-to-5bit encoding.
Each RAW file contains one or more strip(s), so the RAW filesize is either 18*104 bytes (short strip), or 28*104 bytes (long strip), or a multiple thereof (if it contains more than one strip) (although multi-strip games are often stored in separate files for each strip; named file1.raw, file2.raw, etc).
Filesize should be I*30h, with I=1Ch for short strips, and I=2Ch for long strips, or a multiple thereof (if it contains more than one strip). Each strip consists of the 48-byte Data Header, followed by title(s), and vpk compressed data. Unlike .RAW files, .BIN files aren’t interleaved, and do not contain Block Headers, nor error-info, nor unused bytes (in last block). The files do contain padding bytes to match a full strip-size of I*30h.
Caution: Older .BIN files have been using a size-reduced 12-byte header (taken from entries 0Dh, 0Ch, 10h-11h, 26h-2Dh of the 48-byte Data Header; in that order), that files have never contained more than one strip per file, so the filesize should be exactly I*30h-36, the size-reduced header doesn’t contain a Primary Type entry, so it’s everyone’s bet which Card Type is to be used (hint: the 12-byte headers were based on the assumption that Primary Type would be always 01h on Short Strips, and 02h on Long Strips).
Contains a copy of the e-Reader’s 128Kbyte FLASH memory. With the saved e-Reader application being located in the 2nd 64K-bank, the data consists of a header with title and gba/nes/z80 format info, followed by the vpk compressed data. The FLASH memory does also contain e-Reader calibration settings, the remaining 100Kbytes are typically FFh-filled.
No info?
These are some NES/Famicom games ported or emulated to work on GBA. The games are doing some uncommon stuff that can cause compatibility problems when not using original GBA consoles or cartridges.
CPU pipeline (selfmodifying code that shall NOT affect prefetched opcodes)
STMDA write to I/O ports (writes in INCREASING order, not DECREASING order)
SRAM detection (refuses to run if SRAM exists; the games do contain EEPROM)
ROM mirrors (instead of the usual increasing numbers in unused ROM area)
RAM mirrors (eg. main RAM accessed at 2F00000h instead of 2000000h)
Note: These games can be detected by checking [80000ACh]=”F” (ie. game code=”Fxxx”).
Flashcards are re-writable cartridges using FLASH memory, allowing to test even multiboot-incompatible GBA software on real hardware, providing a good development environment when used in combination with a reasonable software debugger.
The carts can be written to from external tools, or directly from GBA programs.
Below are pseudo code flowcharts for detect, erase, and write operations.
All flash reads/writes are meant to be 16bit (ldrh/strh) memory accesses.
configure_flashcard(9E2468Ah,9413h) ;unlock flash advance cards
turbo=1, send_command(8000000h,90h) ;enter ID mode (both chips, if any)
maker=[8000000h], device=[8000000h+2]
IF maker=device THEN device=[8000000h+4] ELSE turbo=0
flashcard_read_mode ;exit ID mode
search (maker+device*10000h) in device_list
total/erase/write_block_size = list_entry SHL turbo
FOR x=1 to len/erase_block_size
send_command(dest,20h) ;erase sector command
send_command(dest,D0h) ;confirm erase sector
dest=dest+erase_block_size
IF wait_busy=okay THEN NEXT x
enter_read_mode ;exit erase/status mode
siz=write_block_size
FOR x=1 to len/siz
IF siz=2 THEN send_command(dest,10h) ;write halfword command
IF siz>2 THEN send_command(dest,E8h) ;write to buffer command
IF siz>2 THEN send_command(dest,16-1) ;buffer size 16 halfwords (per chip)
FOR y=1 TO siz/2
[dest]=[src], dest=dest+2, src=src+2 ;write data to buffer
NEXT y
IF siz>2 THEN send_command(dest,D0h) ;confirm write to buffer
IF wait_busy=okay THEN NEXT x
enter_read_mode ;exit write/status mode
send_command(8000000h,FFh) ;exit status mode
send_command(8000000h,FFh) ;again maybe more stable (as in jeff's source)
configure_flashcard(942468Ah,???)
ID Code Total Erase Write Name
-??-00DCh ? ? ? Hudson Cart (???)
00160089h 4M 128K 32 Intel i28F320J3A (Flash Advance)
00170089h 8M 128K 32 Intel i28F640J3A (Flash Advance)
00180089h 16M 128K 32 Intel i28F128J3A (Flash Advance)
00E200B0h ? 64K 2 Sharp LH28F320BJE ? (Nintendo)
All flashcards should work at 4,2 waitstates (power on default), most commercial games change waits to 3,1 which may work unstable with some/older FA flashcards. Intel FLASH specified to have a lifetime of 100,000 erases, and average block erase time 1 second (up to 5 second in worst cases).
Aside from the main FLASH memory, Flash Advance (FA) (aka Visoly) cards additionally contain battery buffered SRAM backup, and FLASH backup, and in some cases also EEPROM backup.
Turbo FA cards are containing two chips interlaced (at odd/even halfword addresses), allowing to write/erase both chips simultaneously, resulting in twice as fast programming time.
Standard Nintendo flash carts have to be modified before you can actually write to them. This is done by removing resistor R7 and putting it at empty location R8.
Mind that write/erase/detect modes output status information in ROM area, so that in that modes all GBA program code (and any interrupt handlers) must be executed in WRAM, not in ROM.
Thanks to Jeff Frohwein for his FAQ and CARTLIB sample in FLGBA at devrs.com
Codebreaker (US) aka Xploder (EUR).
Gameshark (US) aka Action Replay (EUR).
Cheat devices are external adapters, connected between the GBA and the game cartridge. The devices include a BIOS ROM which is, among others, used to prompt the user to enter cheat codes.
These codes are used to patch specified memory locations for a certain GBA game, allowing the user to gain goodies such like Infinite sex, 255 Cigarettes, etc.
For ROM Patches, the device watches the address bus, if it matches a specified address then it outputs a patched value to the data bus, that mechanism is implemented by hardware, aside from the Hook Enable Code some devices also allow a limited number of cheats to use ROM patches.
Most cheat codes are RAM patches, each time when the hook procedure is executed it will process all codes and overwrite the specified addresses in RAM (or VRAM or I/O area) by the desired values.
Enable codes usually consist of the Game ID, Hook Address, and eventually a third code used to encrypt all following codes. The Game ID is used to confirm that the correct cartridge is inserted, just a verification, though the device may insist on the ID code.
The Hook Address specifies an address in cartridge ROM, and should point to an opcode which is executed several times per second (eg. once per frame, many codes place the hook in the joypad handler). At the hook address, the device redirects to its own BIOS, processes the RAM patches, and does then return control to the game cartridge.
Note: The hook address should not point to opcodes with relative addressing (eg. B, BL, LDR Rd,=Imm, ADD Rd,=Imm opcodes - which are all relative to PC program counter register).
Addresses for 16bit or 32bit values should be properly aligned.
0000xxxx 000y Enable Code 1 - Game ID
1aaaaaaa 000z Enable Code 2 - Hook Address
2aaaaaaa yyyy [aaaaaaa]=[aaaaaaa] OR yyyy
3aaaaaaa 00yy [aaaaaaa]=yy
4aaaaaaa yyyy [aaaaaaa+0..(cccc-1)*ssss]=yyyy+0..(cccc-1)*ssss
iiiicccc ssss parameters for above code
5aaaaaaa cccc [aaaaaaa+0..(cccc-1)]=11,22,33,44,etc.
11223344 5566 parameter bytes 1..6 for above code (example)
77880000 0000 parameter bytes 7..8 for above code (padded with zero)
6aaaaaaa yyyy [aaaaaaa]=[aaaaaaa] AND yyyy
7aaaaaaa yyyy IF [aaaaaaa]=yyyy THEN (next code)
8aaaaaaa yyyy [aaaaaaa]=yyyy
9xyyxxxx xxxx Enable Code 0 - Encrypt all following codes (optional)
Aaaaaaaa yyyy IF [aaaaaaa]<>yyyy THEN (next code)
Baaaaaaa yyyy IF [aaaaaaa]>yyyy THEN (next code) (signed comparison)
Caaaaaaa yyyy IF [aaaaaaa]<yyyy THEN (next code) (signed comparison)
D0000020 yyyy IF [joypad] AND yyyy = 0 THEN (next code)
Eaaaaaaa yyyy [aaaaaaa]=[aaaaaaa]+yyyy
Faaaaaaa yyyy IF [aaaaaaa] AND yyyy THEN (next code)
Hook Address ‘aaaaaaa’ is a 25bit offset in ROM-image (0-1FFFFFFh).
Flag byte ‘y’ (usually 0Ah), Bit1=Disable IRQs, Bit3=CRC Exists.
Code Handler Store Address ‘z’ (0-7, usually 7) (8000100h+z*400000h).
Checksum ‘xxxx’ for first 64Kbytes of cartridge (no$gba pads by FFh if ROM is smaller than 64K). Calculated, by using unsigned 16bit values, as such:
crc=FFFFh
for i=0 to FFFFh
x=byte[i] xor (crc/100h)
x=x xor (x/10h)
crc=(crc*100h) xor (x*1001h) xor (x*20h)
next i
codebreaker_change_encryption:
Encryption can be (optionally) activated by code “9xyyxxxx xxxx”,
for i=0 to 2Fh, swaplist[i]=i, next i
randomizer = 1111h xor byte[code+4] ;LSB value
for i=0 to 4Fh
exchange swaplist[random MOD 30h] with swaplist[random MOD 30h]
next i
halfword[seedlist+0] = halfword[code+0] ;LSW address
randomizer = 4EFAD1C3h
for i=0 to byte[code+3]-91h, randomizer=random, next i ;MSB address
word[seedlist+2]=random, halfword[seedlist+6]=random
randomizer = F254h xor byte[code+5] ;MSB value
for i=0 to byte[code+5]-01h, randomizer=random, next i ;MSB value
word[seedlist+8]=random, halfword[seedlist+12]=random
;note: byte[code+2] = don't care
ret
The above random function works like so:
randomizer=randomizer*41C64E6Dh+3039h, x=(randomizer SHL 14 AND C0000000h)
randomizer=randomizer*41C64E6Dh+3039h, x=(randomizer SHR 1 AND 3FFF8000h)+x
randomizer=randomizer*41C64E6Dh+3039h, x=(randomizer SHR 16 AND 00007FFFh)+x
return(x)
Once when encryption is activated, all following codes are decrypted like so:
for i=2Fh to 0
j=swaplist[i]
bitno1=(i AND 7), index1=xlatlist[i/8]
bitno2=(j AND 7), index2=xlatlist[j/8]
exchange [code+index1].bitno1 with [code+index2].bitno2
next i
word[code+0] = word[code+0] xor word[seedlist+8]
i = (byte[code+3]*1010000h + byte[code+0]*100h + byte[code+5])
i = (halfword[code+1]*10001h) xor (word[seedlist+2]) xor i
i = (byte[seedlist+0]*1010101h) xor (byte[seedlist+1]*1000000h) xor i
j = (byte[code+5] + (byte[code+0] xor byte[code+4])*100h)
j = (byte[seedlist+0]*101h) xor halfword[seedlist+6] xor j
word[code+0] = i, halfword[code+4] = j
The above xlatlist is fixed: xlatlist[0..5] = 3,2,1,0,5,4
0aaaaaaa 000000xx [aaaaaaa]=xx
1aaaaaaa 0000xxxx [aaaaaaa]=xxxx
2aaaaaaa xxxxxxxx [aaaaaaa]=xxxxxxxx
3000cccc xxxxxxxx write xxxxxxxx to (cccc-1) addresses (list in next codes)
aaaaaaaa aaaaaaaa parameter for above code, containing two addresses each
aaaaaaaa 00000000 last parameter for above, zero-padded if only one address
60aaaaaa y000xxxx [8000000h+aaaaaa*2]=xxxx (ROM Patch)
8a1aaaaa 000000xx IF GS_Button_Down THEN [a0aaaaa]=xx
8a2aaaaa 0000xxxx IF GS_Button_Down THEN [a0aaaaa]=xxxx
80F00000 0000xxxx IF GS_Button_Down THEN slowdown xxxx * ? cycles per hook
Daaaaaaa 0000xxxx IF [aaaaaaa]=xxxx THEN (next code)
E0zzxxxx 0aaaaaaa IF [aaaaaaa]=xxxx THEN (next 'zz' codes)
Faaaaaaa 00000x0y Enable Code - Hook Routine
xxxxxxxx 001DC0DE Enable Code - Game Code ID (value at [0ACh] in cartridge)
DEADFACE 0000xxyy Change Encryption Seeds
Hook Address ‘aaaaaaa’ is a 28bit ROM address (8FFFFFFh-9FFFFFFh).
Used to insert the GS code handler routine where it will be executed at
least 20 times per second. Without this code, GSA can not write to RAM.
This type allows GSA to intercept ROM reads and returns the value xxxx.
Note: V1/V2 hardware can only have up to 1 user-defined rom patch max. V3 can have up to 4. Some enable code types can shorten the amount of user-defined rom patches available.
A=Left half, and V=Right half of code.
FOR I=1 TO 32
A=A + (V*16+S0) XOR (V+I*9E3779B9h) XOR (V/32+S1)
V=V + (A*16+S2) XOR (A+I*9E3779B9h) XOR (A/32+S3)
NEXT I
Upon startup, the initial encryption seeds are:
S0=09F4FBBDh S1=9681884Ah S2=352027E9h S3=F3DEE5A7h
Upon DEADFACE 0000xxyy, the S0..S3 seeds are changed like so:
FOR y=0 TO 3
FOR x=0 TO 3
z = T1[(xx+x) AND FFh] + T2[(yy+y) AND FFh]
Sy = Sy*100h + (z AND FFh)
NEXT x
NEXT y
All calculations truncated to unsigned 32bit integer values.
T1 and T2 are translation tables contained in the gameshark cartridge.
C4aaaaaa 0000yyyy Enable Code - Hook Routine at [8aaaaaa]
xxxxxxxx 001DC0DE Enable Code - ID Code [080000AC]
DEADFACE 0000xxxx Enable Code - Change Encryption Seeds
00aaaaaa xxxxxxyy [a0aaaaa..a0aaaaa+xxxxxx]=yy
02aaaaaa xxxxyyyy [a0aaaaa..a0aaaaa+xxxx*2]=yyyy
04aaaaaa yyyyyyyy [a0aaaaa]=yyyyyyyy
40aaaaaa xxxxxxyy [ [a0aaaaa] + xxxxxx ]=yy (Indirect)
42aaaaaa xxxxyyyy [ [a0aaaaa] + xxxx*2 ]=yyyy (Indirect)
44aaaaaa yyyyyyyy [ [a0aaaaa] ]=yyyyyyyy (Indirect)
80aaaaaa 000000yy [a0aaaaa]=[a0aaaaa]+yy
82aaaaaa 0000yyyy [a0aaaaa]=[a0aaaaa]+yyyy
84aaaaaa yyyyyyyy [a0aaaaa]=[a0aaaaa]+yyyyyyyy
C6aaaaaa 0000yyyy [4aaaaaa]=yyyy (I/O Area)
C7aaaaaa yyyyyyyy [4aaaaaa]=yyyyyyyy (I/O Area)
iiaaaaaa yyyyyyyy IF [a0aaaaa] <cond> <value> THEN <action>
00000000 60000000 ELSE (?)
00000000 40000000 ENDIF (?)
00000000 0800xx00 AR Slowdown : loops the AR xx times
00000000 00000000 End of the code list
00000000 10aaaaaa 000000zz 00000000 IF AR_BUTTON THEN [a0aaaaa]=zz
00000000 12aaaaaa 0000zzzz 00000000 IF AR_BUTTON THEN [a0aaaaa]=zzzz
00000000 14aaaaaa zzzzzzzz 00000000 IF AR_BUTTON THEN [a0aaaaa]=zzzzzzzz
00000000 18aaaaaa 0000zzzz 00000000 [8000000+aaaaaa*2]=zzzz (ROM Patch 1)
00000000 1Aaaaaaa 0000zzzz 00000000 [8000000+aaaaaa*2]=zzzz (ROM Patch 2)
00000000 1Caaaaaa 0000zzzz 00000000 [8000000+aaaaaa*2]=zzzz (ROM Patch 3)
00000000 1Eaaaaaa 0000zzzz 00000000 [8000000+aaaaaa*2]=zzzz (ROM Patch 4)
00000000 80aaaaaa 000000yy ssccssss repeat cc times [a0aaaaa]=yy
(with yy=yy+ss, a0aaaaa=a0aaaaa+ssss after each step)
00000000 82aaaaaa 0000yyyy ssccssss repeat cc times [a0aaaaa]=yyyy
(with yyyy=yyyy+ss, a0aaaaa=a0aaaaa+ssss*2 after each step)
00000000 84aaaaaa yyyyyyyy ssccssss repeat cc times [a0aaaaa]=yyyyyyyy
(with yyyy=yyyy+ss, a0aaaaa=a0aaaaa+ssss*4 after each step)
Warning: There is a bug on the real AR (v2 upgraded to v3, and maybe on real v3) with the 32bit Increment Slide code. You HAVE to add a code (best choice is 80000000 00000000 : add 0 to value at address 0) right after it, else the AR will erase the 2 last 8 digits lines of the 32 Bits Inc. Slide code when you enter it !!!
Final Notes
The ‘turn off all codes’ makes an infinite loop (that can’t be broken, unless the condition becomes True). - How? By Interrupt? Huh?
ROM Patch1 works on real V3 and, on V1/V2 upgraded to V3.
ROM Patch2,3,4 work on real V3 hardware only.
The ‘ii’ is composed of <cond> + <value> + <action>.
<cond> <value> <action>
08 Equal = 00 8bit zz 00 execute next code
10 Not equal <> 02 16bit zzzz 40 execute next two codes
18 Signed < 04 32bit zzzzzzzz 80 execute all following
20 Signed > 06 (always false) codes until ELSE or ENDIF
28 Unsigned < C0 normal ELSE turn off all codes
30 Unsigned >
38 Logical AND
For example, ii=18h+02h+40h=5Ah, produces IF [a0aaaaa]<zzzz THEN next 2 codes.
Always… Codes
For the "Always..." codes:
- XXXXXXXX can be any authorised address except 00000000 (eg. use 02000000).
- ZZZZZZZZ can be anything.
- The "y" in the code data must be in the [1-7] range (which means not 0).
typ=y,sub=0,siz=3 Always skip next line.
typ=y,sub=1,siz=3 Always skip next 2 lines.
typ=y,sub=2,siz=3 Always Stops executing all the codes below.
typ=y,sub=3,siz=3 Always turn off all codes.
Works exactly as for Gameshark Encryption, but with different initial seeds,
S0=7AA9648Fh S1=7FAE6994h S2=C0EFAAD5h S3=42712C57h
And, the T1 and T2 translation tables are different, too.
The Gameboy Player is an “adapter” for the Gamecube console. It’s basicly is a GBA in a black box without LCD screen and without buttons, connected to an expansion port at the bottom of the Gamecube. The Gamecube is then capturing the GBA video output (and passing it to the television set), and in the other direction, passing the Gamecube joypad input to the GBA inputs.
Both unlocking and detection requires to display the 240x160 pixel Gameboy Player logo (44 colors) for a number of frames… maybe at least 3-4 frames? not sure if it checks the color of the logo… so maybe it can be hidden by using dark gray on black background?
While displaying this logo, the joypad data will switch between values 03FFh (2 frames duration) and 030Fh (1 frame duration). The latter value (left, right, up, down all pressed) indicates that it’s a Gameboy Player.
Knowing Nintendo, they’ve probably not reproduced the blurred GBA colors (?), so the games won’t look as desired on the TV screen. Unless the game does detect the Gameboy Player, and adjust the colors accordingly by software.
The only known existing special function is the joypad rumble function, controlled by sending data through the serial port (the normal GBA port, even though it also has the connectors).
The Game Boy Player added a rumble feature to certain Game Boy Advance games when played with a GameCube controller. Those games included:
Fredrik Olsson (aka Flubba) has implemented rumble in 3 applications now RumblePong (FluBBA) (homebrew)
Remudvance (FluBBA) (homebrew)
Goomba (FluBBA) (8bit Gameboy Color Emulator for 32bit GBA) (homebrew)
and, supposedly in "Tetanus on Drugs" (Tepples) (homebrew)
The GBP can also use some of the extra controllers for the GC like the Bongas
from Donkey Konga.
The logo requires at least 256 colors, it doesn’t matter if you use a tiled
screen mode or a bitmapped one, the logo can be ripped from either
“Pokemon Pinball” or “Super Mario Advance 4”.
After detecting/unlocking the Gameboy Player, init RCNT and SIOCNT to 32bit normal mode, external clock, SO=high, with IRQ enabled, and set the transfer start bit. You should then receive the following sequence (about once per frame), and your serial IRQ handler should send responses accordingly:
Receive Response
0000494E 494EB6B1
xxxx494E 494EB6B1
B6B1494E 544EB6B1
B6B1544E 544EABB1
ABB1544E 4E45ABB1
ABB14E45 4E45B1BA
B1BA4E45 4F44B1BA
B1BA4F44 4F44B0BB
B0BB4F44 8000B0BB
B0BB8002 10000010
10000010 20000013
20000013 40000004
30000003 40000004
30000003 40000004
30000003 40000004
30000003 400000yy
30000003 40000004
The first part of the transfer just contains the string “NINTENDO” split into 16bit fragments, and bitwise inversions thereof (eg. 494Eh=”NI”, and B6B1h=NOT 494Eh). In the second part, <yy> should be 04h=RumbleOff, or 26h=RumbleOn.
If it’s having a similar range of functions as the 8bit Super Gameboy, then the Gameboy Player might be also able to access analogue joypad input, and to access other features of the Gamecube hardware, up to possibly executing code on the Gamecube CPU…?
Most of the below is caused by ‘traces’ from previous operations which have used the databus. No promises that the results are stable on all current or future GBA models, and/or under all temperature and interference circumstances.
Also, below specifies 32bit data accesses only. When reading units less than 32bit, data is rotated depending on the alignment of the originally specified address, and 8bit or 16bit are then isolated from the 32bit value as usually.
The BIOS memory is protected against reading, the GBA allows to read opcodes or data only if the program counter is located inside of the BIOS area. If the program counter is not in the BIOS area, reading will return the most recent successfully fetched BIOS opcode (eg. the opcode at [00DCh+8] after startup and SoftReset, the opcode at [0134h+8] during IRQ execution, and opcode at [013Ch+8] after IRQ execution, and opcode at [0188h+8] after SWI execution).
Accessing unused memory at 00004000h-01FFFFFFh, and 10000000h-FFFFFFFFh (and 02000000h-03FFFFFFh when RAM is disabled via Port 4000800h) returns the recently pre-fetched opcode. For ARM code this is simply:
WORD = [$+8]
For THUMB code the result consists of two 16bit fragments and depends on the address area and alignment where the opcode was stored.
For THUMB code in Main RAM, Palette Memory, VRAM, and Cartridge ROM this is:
LSW = [$+4], MSW = [$+4]
For THUMB code in BIOS or OAM (and in 32K-WRAM on Original-NDS (in GBA mode)):
LSW = [$+4], MSW = [$+6] ;for opcodes at 4-byte aligned locations
LSW = [$+2], MSW = [$+4] ;for opcodes at non-4-byte aligned locations
For THUMB code in 32K-WRAM on GBA, GBA SP, GBA Micro, NDS-Lite (but not NDS):
LSW = [$+4], MSW = OldHI ;for opcodes at 4-byte aligned locations
LSW = OldLO, MSW = [$+4] ;for opcodes at non-4-byte aligned locations
Whereas OldLO/OldHI are usually:
OldLO=[$+2], OldHI=[$+2]
Unless the previous opcode’s prefetch was overwritten; that can happen if the previous opcode was itself an LDR opcode, ie. if it was itself reading data:
OldLO=LSW(data), OldHI=MSW(data)
Theoretically, this might also change if a DMA transfer occurs.
Note: Additionally, as usually, the 32bit data value will be rotated if the data address wasn’t 4-byte aligned, and the upper bits of the 32bit value will be masked in case of LDRB/LDRH reads.
Note: The opcode prefetch is caused by the prefetch pipeline in the CPU itself, not by the external gamepak prefetch, ie. it works for code in ROM and RAM as well.
Works like above Unused Memory when the entire 32bit memory fragment is Unused (eg. 0E0h) and/or Write-Only (eg. DMA0SAD). And otherwise, returns zero if the lower 16bit fragment is readable (eg. 04Ch=MOSAIC, 04Eh=NOTUSED/ZERO).
Because Gamepak uses the same signal-lines for both 16bit data and for lower 16bit halfword address, the entire gamepak ROM area is effectively filled by incrementing 16bit values (Address/2 AND FFFFh).
Most internal memory is mirrored across the whole 24bit/16MB address space in which it is located: Slow On-board RAM at 2XXXXXX, Fast On-Chip RAM at 3XXXXXXh, Palette RAM at 5XXXXXXh, VRAM at 6XXXXXXh, and OAM at 7XXXXXXh. Even though VRAM is sized 96K (64K+32K), it is repeated in steps of 128K (64K+32K+32K, the two 32K blocks itself being mirrors of each other).
BIOS ROM, Normal ROM Cartridges, and I/O area are NOT mirrored, the only exception is the undocumented I/O port at 4000800h (repeated each 64K).
The 64K SRAM area is mirrored across the whole 32MB area at E000000h-FFFFFFFh, also, inside of the 64K SRAM field, 32K SRAM chips are repeated twice.
Video Memory (BG, OBJ, OAM, Palette) can be written to in 16bit and 32bit units only. Attempts to write 8bit data (by STRB opcode) won’t work:
Writes to OBJ (6010000h-6017FFFh) (or 6014000h-6017FFFh in Bitmap mode) and to OAM (7000000h-70003FFh) are ignored, the memory content remains unchanged.
Writes to BG (6000000h-600FFFFh) (or 6000000h-6013FFFh in Bitmap mode) and to Palette (5000000h-50003FFh) are writing the new 8bit value to BOTH upper and lower 8bits of the addressed halfword, ie. “[addr AND NOT 1]=data*101h”.
In Text mode, large tile numbers (combined with a non-zero character base setting in BGnCNT register) may exceed the available 64K of BG VRAM.
On GBA and GBA SP, such invalid tiles are displayed as if the character data is filled by the 16bit BG Map entry value (ie. as vertically striped tiles). Above applies only if there is only one BG layer enabled, with two or more layers, things are getting much more complicated: tile-data is then somehow derived from the other layers, depending on their priority order and scrolling offsets.
On NDS (in GBA mode), such invalid tiles are displayed as if the character data is zero-filled (ie. as invisible/transparent tiles).
Reading retrieves 8bit value from specified address, multiplied by 0101h (LDRH) or by 01010101h (LDR). Writing changes the 8bit value at the specified address only, being set to LSB of (source_data ROR (address*8)).
1x ARM946E-S 32bit RISC CPU, 66MHz (NDS9 video) (not used in GBA mode)
1x ARM7TDMI 32bit RISC CPU, 33MHz (NDS7 sound) (16MHz in GBA mode)
4096KB Main RAM (8192KB in debug version)
96KB WRAM (64K mapped to NDS7, plus 32K mappable to NDS7 or NDS9)
60KB TCM/Cache (TCM: 16K Data, 32K Code) (Cache: 4K Data, 8K Code)
656KB VRAM (allocateable as BG/OBJ/2D/3D/Palette/Texture/WRAM memory)
4KB OAM/PAL (2K OBJ Attribute Memory, 2K Standard Palette RAM)
248KB Internal 3D Memory (104K Polygon RAM, 144K Vertex RAM)
?KB Matrix Stack, 48 scanline cache
8KB Wifi RAM
256KB Firmware FLASH (512KB in iQue variant, with chinese charset)
36KB BIOS ROM (4K NDS9, 16K NDS7, 16K GBA)
2x LCD screens (each 256x192 pixel, 3 inch, 18bit color depth, backlight)
2x 2D video engines (extended variants of the GBA's video controller)
1x 3D video engine (can be assigned to upper or lower screen)
1x video capture (for effects, or for forwarding 3D to the 2nd 2D engine)
16 sound channels (16x PCM8/PCM16/IMA-ADPCM, 6x PSG-Wave, 2x PSG-Noise)
2 sound capture units (for echo effects, etc.)
Output: Two built-in stereo speakers, and headphones socket
Input: One built-in microphone, and microphone socket
Gamepad 4 Direction Keys, 8 Buttons
Touchscreen (on lower LCD screen)
Wifi IEEE802.11b
Built-in Real Time Clock
Power Managment Device
Hardware divide and square root functions
CP15 System Control Coprocessor (cache, tcm, pu, bist, etc.)
NDS Slot (for NDS games) (encrypted 8bit data bus, and serial 1bit bus)
GBA Slot (for NDS expansions, or for GBA games) (but not for DMG/CGB games)
ROM: 16MB, 32MB, or 64MB
EEPROM/FLASH/FRAM: 0.5KB, 8KB, 64KB, 256KB, or 512KB
NDS Cartridge (NDS mode)
Firmware FLASH (NDS mode) (eg. by patching firmware via ds-xboo cable)
Wifi (NDS mode)
GBA Cartridge (GBA mode) (without DMG/CGB support) (without SIO support)
Built-in rechargeable Lithium ion battery, 3.7V 1000mAh (DS-Lite)
External Supply: 5.2V DC
Slightly smaller than the original NDS, coming in a more decently elegant case. The LCDs are much more colorful (and thus not backwards compatible with any older NDS or GBA games), and the LCDs support wider viewing angles. Slightly different power managment device (with selectable backlight brightness, new external power source flag, lost audio amplifier mute flag). Slightly different Wifi controller (different chip ID, different dirt effects when accessing invalid wifi ports and unused wifi memory regions, different behaviour on GAPDISP registers, RF/BB chips replaced by a single chip). Slightly different touch screen controller (with new unused input, and slightly different powerdown bits).
NDS9 means the ARM9 processor and its memory and I/O ports in NDS mode
NDS7 means the ARM7 processor and its memory and I/O ports in NDS mode
GBA means the ARM7 processor and its memory and I/O ports in GBA mode
Most game code is usually executed on the ARM9 processor (in fact, Nintendo reportedly doesn’t allow developers use the ARM7 processor, except by predefined API functions, anyways, even with the most likely inefficient API code, most of the ARM7’s 33MHz horsepower is left unused).
The ARM9’s 66MHz “horsepower” is a different tale - it seems Nintendo thought that a 33MHz processor would be too “slow” for 3D games, and so they (tried to) badge an additional CPU to the original GBA hardware.
However, the real 66MHz can be used only with cache and tcm, all other memory and I/O accesses are delayed to the 33MHz bus clock, that’d be still quite fast, but, there seems to be a hardware glitch that adds 3 waitcycles to all nonsequential accesses at the NDS9 side, which effectively drops its bus clock to about 8MHz, making it ways slower than the 33MHz NDS7 processor, it’s even slower than the original 16MHz GBA processor.
Altogether, with the bugged 66MHz, and the unused 33MHz, Nintendo could have reached almost the same power when staying with the GBA’s 16MHz processor :-)
Although, when properly using cache/tcm, then the 66MHz processor <can> be very fast, still, the NDS should have worked as well with a single processor, though using only an ARM9 might cause a lot of compatibility problems with GBA games, so there’s at least one reason for keeping the ARM7 included.
ARM9 I/O Map
4000000h 4 2D Engine A - DISPCNT - LCD Control (Read/Write)
4000004h 2 2D Engine A+B - DISPSTAT - General LCD Status (Read/Write)
4000006h 2 2D Engine A+B - VCOUNT - Vertical Counter (Read only)
4000008h 50h 2D Engine A (same registers as GBA, some changed bits)
4000060h 2 DISP3DCNT - 3D Display Control Register (R/W)
4000064h 4 DISPCAPCNT - Display Capture Control Register (R/W)
4000068h 4 DISP_MMEM_FIFO - Main Memory Display FIFO (R?/W)
400006Ch 2 2D Engine A - MASTER_BRIGHT - Master Brightness Up/Down
40000B0h 30h DMA Channel 0..3
40000E0h 10h DMA FILL Registers for Channel 0..3
4000100h 10h Timers 0..3
4000130h 2 KEYINPUT
4000132h 2 KEYCNT
4000180h 2 IPCSYNC - IPC Synchronize Register (R/W)
4000184h 2 IPCFIFOCNT - IPC Fifo Control Register (R/W)
4000188h 4 IPCFIFOSEND - IPC Send Fifo (W)
40001A0h 2 AUXSPICNT - Gamecard ROM and SPI Control
40001A2h 2 AUXSPIDATA - Gamecard SPI Bus Data/Strobe
40001A4h 4 Gamecard bus timing/control
40001A8h 8 Gamecard bus 8-byte command out
40001B0h 4 Gamecard Encryption Seed 0 Lower 32bit
40001B4h 4 Gamecard Encryption Seed 1 Lower 32bit
40001B8h 2 Gamecard Encryption Seed 0 Upper 7bit (bit7-15 unused)
40001BAh 2 Gamecard Encryption Seed 1 Upper 7bit (bit7-15 unused)
4000204h 2 EXMEMCNT - External Memory Control (R/W)
4000208h 2 IME - Interrupt Master Enable (R/W)
4000210h 4 IE - Interrupt Enable (R/W)
4000214h 4 IF - Interrupt Request Flags (R/W)
4000240h 1 VRAMCNT_A - VRAM-A (128K) Bank Control (W)
4000241h 1 VRAMCNT_B - VRAM-B (128K) Bank Control (W)
4000242h 1 VRAMCNT_C - VRAM-C (128K) Bank Control (W)
4000243h 1 VRAMCNT_D - VRAM-D (128K) Bank Control (W)
4000244h 1 VRAMCNT_E - VRAM-E (64K) Bank Control (W)
4000245h 1 VRAMCNT_F - VRAM-F (16K) Bank Control (W)
4000246h 1 VRAMCNT_G - VRAM-G (16K) Bank Control (W)
4000247h 1 WRAMCNT - WRAM Bank Control (W)
4000248h 1 VRAMCNT_H - VRAM-H (32K) Bank Control (W)
4000249h 1 VRAMCNT_I - VRAM-I (16K) Bank Control (W)
4000280h 2 DIVCNT - Division Control (R/W)
4000290h 8 DIV_NUMER - Division Numerator (R/W)
4000298h 8 DIV_DENOM - Division Denominator (R/W)
40002A0h 8 DIV_RESULT - Division Quotient (=Numer/Denom) (R)
40002A8h 8 DIVREM_RESULT - Division Remainder (=Numer MOD Denom) (R)
40002B0h 2 SQRTCNT - Square Root Control (R/W)
40002B4h 4 SQRT_RESULT - Square Root Result (R)
40002B8h 8 SQRT_PARAM - Square Root Parameter Input (R/W)
4000300h 4 POSTFLG - Undoc
4000304h 2 POWCNT1 - Graphics Power Control Register (R/W)
4000320h..6A3h
4001000h 4 2D Engine B - DISPCNT - LCD Control (Read/Write)
4001008h 50h 2D Engine B (same registers as GBA, some changed bits)
400106Ch 2 2D Engine B - MASTER_BRIGHT - 16bit - Brightness Up/Down
40021Axh .. DSi Registers
4004xxxh .. DSi Registers
4100000h 4 IPCFIFORECV - IPC Receive Fifo (R)
4100010h 4 Gamecard bus 4-byte data in, for manual or dma read
4FFF0xxh .. Ensata Emulator Debug Registers
4FFFAxxh .. No$gba Emulator Debug Registers
27FFD9Ch .. NDS9 Debug Stacktop / Debug Vector (0=None)
DTCM+3FF8h 4 NDS9 IRQ Check Bits (hardcoded RAM address)
DTCM+3FFCh 4 NDS9 IRQ Handler (hardcoded RAM address)
27FFFFEh 2 Main Memory Control
4000004h 2 DISPSTAT
4000006h 2 VCOUNT
40000B0h 30h DMA Channels 0..3
4000100h 10h Timers 0..3
4000120h 4 Debug SIODATA32
4000128h 4 Debug SIOCNT
4000130h 2 KEYINPUT
4000132h 2 KEYCNT
4000134h 2 Debug RCNT
4000136h 2 EXTKEYIN
4000138h 1 RTC Realtime Clock Bus
4000180h 2 IPCSYNC - IPC Synchronize Register (R/W)
4000184h 2 IPCFIFOCNT - IPC Fifo Control Register (R/W)
4000188h 4 IPCFIFOSEND - IPC Send Fifo (W)
40001A0h 2 AUXSPICNT - Gamecard ROM and SPI Control
40001A2h 2 AUXSPIDATA - Gamecard SPI Bus Data/Strobe
40001A4h 4 Gamecard bus timing/control
40001A8h 8 Gamecard bus 8-byte command out
40001B0h 4 Gamecard Encryption Seed 0 Lower 32bit
40001B4h 4 Gamecard Encryption Seed 1 Lower 32bit
40001B8h 2 Gamecard Encryption Seed 0 Upper 7bit (bit7-15 unused)
40001BAh 2 Gamecard Encryption Seed 1 Upper 7bit (bit7-15 unused)
40001C0h 2 SPI bus Control (Firmware, Touchscreen, Powerman)
40001C2h 2 SPI bus Data
4000204h 2 EXMEMSTAT - External Memory Status
4000206h 2 WIFIWAITCNT
4000208h 4 IME - Interrupt Master Enable (R/W)
4000210h 4 IE - Interrupt Enable (R/W)
4000214h 4 IF - Interrupt Request Flags (R/W)
4000218h - IE2 ;\DSi only (additional ARM7 interrupt sources)
400021Ch - IF2 ;/
4000240h 1 VRAMSTAT - VRAM-C,D Bank Status (R)
4000241h 1 WRAMSTAT - WRAM Bank Status (R)
4000300h 1 POSTFLG
4000301h 1 HALTCNT (different bits than on GBA) (plus NOP delay)
4000304h 2 POWCNT2 Sound/Wifi Power Control Register (R/W)
4000308h 4 BIOSPROT - Bios-data-read-protection address
4000400h 100h Sound Channel 0..15 (10h bytes each)
40004x0h 4 SOUNDxCNT - Sound Channel X Control Register (R/W)
40004x4h 4 SOUNDxSAD - Sound Channel X Data Source Register (W)
40004x8h 2 SOUNDxTMR - Sound Channel X Timer Register (W)
40004xAh 2 SOUNDxPNT - Sound Channel X Loopstart Register (W)
40004xCh 4 SOUNDxLEN - Sound Channel X Length Register (W)
4000500h 2 SOUNDCNT - Sound Control Register (R/W)
4000504h 2 SOUNDBIAS - Sound Bias Register (R/W)
4000508h 1 SNDCAP0CNT - Sound Capture 0 Control Register (R/W)
4000509h 1 SNDCAP1CNT - Sound Capture 1 Control Register (R/W)
4000510h 4 SNDCAP0DAD - Sound Capture 0 Destination Address (R/W)
4000514h 2 SNDCAP0LEN - Sound Capture 0 Length (W)
4000518h 4 SNDCAP1DAD - Sound Capture 1 Destination Address (R/W)
400051Ch 2 SNDCAP1LEN - Sound Capture 1 Length (W)
40021Axh .. DSi Registers
4004xxxh .. DSi Registers
4004700h 2 DSi SNDEXCNT Register ;\mapped even in DS mode
4004C0xh .. DSi GPIO Registers ;/
4100000h 4 IPCFIFORECV - IPC Receive Fifo (R)
4100010h 4 Gamecard bus 4-byte data in, for manual or dma read
4700000h 4 Disable ARM7 bootrom overlay (W) (3DS only)
4800000h .. Wifi WS0 Region (32K) (Wifi Ports, and 8K Wifi RAM)
4808000h .. Wifi WS1 Region (32K) (mirror of above, other waitstates)
380FFC0h 4 DSi7 IRQ IF2 Check Bits (hardcoded RAM address) (DSi only)
380FFDCh .. NDS7 Debug Stacktop / Debug Vector (0=None)
380FFF8h 4 NDS7 IRQ IF Check Bits (hardcoded RAM address)
380FFFCh 4 NDS7 IRQ Handler (hardcoded RAM address)
00000000h Instruction TCM (32KB) (not moveable) (mirror-able to 1000000h)
0xxxx000h Data TCM (16KB) (moveable)
02000000h Main Memory (4MB)
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM9)
04000000h ARM9-I/O Ports
05000000h Standard Palettes (2KB) (Engine A BG/OBJ, Engine B BG/OBJ)
06000000h VRAM - Engine A, BG VRAM (max 512KB)
06200000h VRAM - Engine B, BG VRAM (max 128KB)
06400000h VRAM - Engine A, OBJ VRAM (max 256KB)
06600000h VRAM - Engine B, OBJ VRAM (max 128KB)
06800000h VRAM - "LCDC"-allocated (max 656KB)
07000000h OAM (2KB) (Engine A, Engine B)
08000000h GBA Slot ROM (max 32MB)
0A000000h GBA Slot RAM (max 64KB)
FFFF0000h ARM9-BIOS (32KB) (only 3K used)
The ARM9 Exception Vectors are located at FFFF0000h. The IRQ handler redirects to [DTCM+3FFCh].
00000000h ARM7-BIOS (16KB)
02000000h Main Memory (4MB)
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM7)
03800000h ARM7-WRAM (64KB)
04000000h ARM7-I/O Ports
04800000h Wireless Communications Wait State 0 (8KB RAM at 4804000h)
04808000h Wireless Communications Wait State 1 (I/O Ports at 4808000h)
06000000h VRAM allocated as Work RAM to ARM7 (max 256K)
08000000h GBA Slot ROM (max 32MB)
0A000000h GBA Slot RAM (max 64KB)
The ARM7 Exception Vectors are located at 00000000h. The IRQ handler redirects to [3FFFFFCh aka 380FFFCh].
3D Engine Polygon RAM (52KBx2)
3D Engine Vertex RAM (72KBx2)
Firmware (256KB) (built-in serial flash memory)
GBA-BIOS (16KB) (not used in NDS mode)
NDS Slot ROM (serial 8bit-bus, max 4GB with default protocol)
NDS Slot FLASH/EEPROM/FRAM (serial 1bit-bus)
Even though Shared WRAM begins at 3000000h, programs are commonly using mirrors at 37F8000h (both ARM9 and ARM7). At the ARM7-side, this allows to use 32K Shared WRAM and 64K ARM7-WRAM as a continous 96K RAM block.
On the NDS (at the ARM9-side at least) undefined I/O ports are always zero.
16MB blocks that do not contain any defined memory regions (or that contain only mapped TCM regions) are typically completely undefined.
16MB blocks that do contain valid memory regions are typically containing mirrors of that memory in the unused upper part of the 16MB area (only exceptions are TCM and BIOS which are not mirrored).
TCM and Cache are controlled by the System Control Coprocessor,
The specifications for the NDS9 are:
ITCM 32K, base=00000000h (fixed, not move-able)
DTCM 16K, base=moveable (default base=27C0000h)
Note: Although ITCM is NOT moveable, the NDS Firmware configures the ITCM size to 32MB, and so, produces ITCM mirrors at 0..1FFFFFFh. Furthermore, the PU can be used to lock/unlock memory in that region. That trick allows to move ITCM anywhere within the lower 32MB of memory.
Data Cache 4KB, Instruction Cache 8KB
4-way set associative method
Cache line 8 words (32 bytes)
Read-allocate method (ie. writes are not allocating cache lines)
Round-robin and Pseudo-random replacement algorithms selectable
Cache Lockdown, Instruction Prefetch, Data Preload
Data write-through and write-back modes selectable
Recommended/default settings are:
Region Name Address Size Cache WBuf Code Data
- Background 00000000h 4GB - - - -
0 I/O and VRAM 04000000h 64MB - - R/W R/W
1 Main Memory 02000000h 4MB On On R/W R/W
2 ARM7-dedicated 027C0000h 256KB - - - -
3 GBA Slot 08000000h 128MB - - - R/W
4 DTCM 027C0000h 16KB - - - R/W
5 ITCM 01000000h 32KB - - R/W R/W
6 BIOS FFFF0000h 32KB On - R R
7 Shared Work 027FF000h 4KB - - - R/W
Notes: In Nintendo’s hardware-debugger, Main Memory is expanded to 8MB (for that reason, some addresses are at 27NN000h instead 23NN000h) (some of the extra memory is reserved for the debugger, some can be used for game development). Region 2 and 7 are not understood? GBA Slot should be max 32MB+64KB, rounded up to 64MB, no idea why it is 128MB? DTCM and ITCM do not use Cache and Write-Buffer because TCM is fast. Above settings do not allow to access Shared Memory at 37F8000h? Do not use cache/wbuf for I/O, doing so might suppress writes, and/or might read outdated values.
The main purpose of the Protection Unit is debugging, a major problem with GBA programs have been faulty accesses to memory address 00000000h and up (due to [base+offset] addressing with uninitialized (zero) base values). This problem has been fixed in the NDS, for the ARM9 processor at least, still there are various leaks: For example, the 64MB I/O and VRAM area contains only ca. 660KB valid addresses, and the ARM7 probably doesn’t have a Protection Unit at all. Alltogether, the protection is better than in GBA, but it’s still pretty crude compared with software debugging tools.
Region address/size are unified (same for code and data), however, cachabilty and access rights are non-unified (and may be separately defined for code and data).
Note: The NDS7 doesn’t have any TCM, Cache, or CP15.
0-1 32-pin GBA Slot SRAM Access Time (0-3 = 10, 8, 6, 18 cycles)
2-3 32-pin GBA Slot ROM 1st Access Time (0-3 = 10, 8, 6, 18 cycles)
4 32-pin GBA Slot ROM 2nd Access Time (0-1 = 6, 4 cycles)
5-6 32-pin GBA Slot PHI-pin out (0-3 = Low, 4.19MHz, 8.38MHz, 16.76MHz)
7 32-pin GBA Slot Access Rights (0=ARM9, 1=ARM7)
8-10 Not used (always zero)
11 17-pin NDS Slot Access Rights (0=ARM9, 1=ARM7)
12 Not used (always zero)
13 NDS:Always set? ;set/tested by DSi bootcode: Main RAM enable, CE2 pin?
14 Main Memory Interface Mode Switch (0=Async/GBA/Reserved, 1=Synchronous)
15 Main Memory Access Priority (0=ARM9 Priority, 1=ARM7 Priority)
Bit0-6 can be changed by both NDS9 and NDS7, changing these bits affects the local EXMEM register only, not that of the other CPU.
Bit7-15 can be changed by NDS9 only, changing these bits affects both EXMEM registers, ie. both NDS9 and NDS7 can read the current NDS9 setting.
Bit14=0 is intended for GBA mode, however, writes to this bit appear to be ignored?
The GBA Slot can be mapped to ARM9 or ARM7 via EXMEMCNT.7.
For the selected CPU, memory at 8000000h-9FFFFFFh contains the “GBA ROM” region, and memory at A000000h-AFFFFFFh contains the “GBA SRAM” region (repeated every 64Kbytes). If there is no cartridge in GBA Slot, then the ROM/SRAM regions will contain open-bus values: SRAM region is FFh-filled (High-Z). And ROM region is filled by increasing 16bit values (Addr/2), possibly ORed with garbage depending on the selected ROM Access Time:
6 clks --> returns "Addr/2"
8 clks --> returns "Addr/2"
10 clks --> returns "Addr/2 OR FE08h" (or similar garbage)
18 clks --> returns "FFFFh" (High-Z)
For the deselected CPU, all memory at 8000000h-AFFFFFFh becomes 00h-filled, this is required for bugged games like Digimon Story: Super Xros Wars (which is accidently reading deselected GBA SRAM at [main_ram_base+main_ram_addr*4], whereas it does presumably want to read Main RAM at [main_ram_base+index*4]).
Should not be changed when using Nintendo’s API.
0-1 ARM9/ARM7 (0-3 = 32K/0K, 2nd 16K/1st 16K, 1st 16K/2nd 16K, 0K/32K)
2-7 Not used
The ARM9 WRAM area is 3000000h-3FFFFFFh (16MB range).
The ARM7 WRAM area is 3000000h-37FFFFFh (8MB range).
The allocated 16K or 32K are mirrored everywhere in the above areas.
De-allocation (0K) is a special case: At the ARM9-side, the WRAM area is then empty (containing undefined data). At the ARM7-side, the WRAM area is then containing mirrors of the 64KB ARM7-WRAM (the memory at 3800000h and up).
0 VRAM C enabled and allocated to NDS7 (0=No, 1=Yes)
1 VRAM D enabled and allocated to NDS7 (0=No, 1=Yes)
2-7 Not used (always zero)
The register indicates if VRAM C/D are allocated to NDS7 (as Work RAM), ie. if VRAMCNT_C/D are enabled (Bit7=1), with MST=2 (Bit0-2). However, it does not reflect the OFS value.
0-2 VRAM MST ;Bit2 not used by VRAM-A,B,H,I
3-4 VRAM Offset (0-3) ;Offset not used by VRAM-E,H,I
5-6 Not used
7 VRAM Enable (0=Disable, 1=Enable)
There is a total of 656KB of VRAM in Blocks A-I.
Table below shows the possible configurations.
VRAM SIZE MST OFS ARM9, Plain ARM9-CPU Access (so-called LCDC mode)
A 128K 0 - 6800000h-681FFFFh
B 128K 0 - 6820000h-683FFFFh
C 128K 0 - 6840000h-685FFFFh
D 128K 0 - 6860000h-687FFFFh
E 64K 0 - 6880000h-688FFFFh
F 16K 0 - 6890000h-6893FFFh
G 16K 0 - 6894000h-6897FFFh
H 32K 0 - 6898000h-689FFFFh
I 16K 0 - 68A0000h-68A3FFFh
VRAM SIZE MST OFS ARM9, 2D Graphics Engine A, BG-VRAM (max 512K)
A,B,C,D 128K 1 0..3 6000000h+(20000h*OFS)
E 64K 1 - 6000000h
F,G 16K 1 0..3 6000000h+(4000h*OFS.0)+(10000h*OFS.1)
VRAM SIZE MST OFS ARM9, 2D Graphics Engine A, OBJ-VRAM (max 256K)
A,B 128K 2 0..1 6400000h+(20000h*OFS.0) ;(OFS.1 must be zero)
E 64K 2 - 6400000h
F,G 16K 2 0..3 6400000h+(4000h*OFS.0)+(10000h*OFS.1)
VRAM SIZE MST OFS 2D Graphics Engine A, BG Extended Palette
E 64K 4 - Slot 0-3 ;only lower 32K used
F,G 16K 4 0..1 Slot 0-1 (OFS=0), Slot 2-3 (OFS=1)
VRAM SIZE MST OFS 2D Graphics Engine A, OBJ Extended Palette
F,G 16K 5 - Slot 0 ;16K each (only lower 8K used)
VRAM SIZE MST OFS Texture/Rear-plane Image
A,B,C,D 128K 3 0..3 Slot OFS(0-3) ;(Slot2-3: Texture, or Rear-plane)
VRAM SIZE MST OFS Texture Palette
E 64K 3 - Slots 0-3 ;OFS=don't care
F,G 16K 3 0..3 Slot (OFS.0*1)+(OFS.1*4) ;ie. Slot 0, 1, 4, or 5
VRAM SIZE MST OFS ARM9, 2D Graphics Engine B, BG-VRAM (max 128K)
C 128K 4 - 6200000h
H 32K 1 - 6200000h
I 16K 1 - 6208000h
VRAM SIZE MST OFS ARM9, 2D Graphics Engine B, OBJ-VRAM (max 128K)
D 128K 4 - 6600000h
I 16K 2 - 6600000h
VRAM SIZE MST OFS 2D Graphics Engine B, BG Extended Palette
H 32K 2 - Slot 0-3
VRAM SIZE MST OFS 2D Graphics Engine B, OBJ Extended Palette
I 16K 3 - Slot 0 ;(only lower 8K used)
VRAM SIZE MST OFS <ARM7>, Plain <ARM7>-CPU Access
C,D 128K 2 0..1 6000000h+(20000h*OFS.0) ;OFS.1 must be zero
In Plain-CPU modes, VRAM can be accessed only by the CPU (and by the Capture Unit, and by VRAM Display mode). In “Plain <ARM7>-CPU Access” mode, the VRAM blocks are allocated as Work RAM to the NDS7 CPU.
In BG/OBJ VRAM modes, VRAM can be accessed by the CPU at specified addresses, and by the display controller.
In Extended Palette and Texture Image/Palette modes, VRAM is not mapped to CPU address space, and can be accessed only by the display controller (so, to initialize or change the memory, it should be temporarily switched to Plain-CPU mode).
All VRAM (and Palette, and OAM) can be written to only in 16bit and 32bit units (STRH, STR opcodes), 8bit writes are ignored (by STRB opcode). The only exception is “Plain <ARM7>-CPU Access” mode: The ARM7 CPU can use STRB to write to VRAM (the reason for this special feature is that, in GBA mode, two 128K VRAM blocks are used to emulate the GBA’s 256K Work RAM).
Aside from the map-able VRAM blocks, there are also some video-related memory regions at fixed addresses:
5000000h Engine A Standard BG Palette (512 bytes)
5000200h Engine A Standard OBJ Palette (512 bytes)
5000400h Engine B Standard BG Palette (512 bytes)
5000600h Engine B Standard OBJ Palette (512 bytes)
7000000h Engine A OAM (1024 bytes)
7000400h Engine B OAM (1024 bytes)
Used to double-protect the first some KBytes of the NDS7 BIOS. The BIOS is split into two protection regions, one always active, one controlled by the BIOSPROT register. The overall idea is that only the BIOS can read from itself, any other attempts to read from that regions return FFh-bytes.
Opcodes at... Can read from Expl.
0..[BIOSPROT]-1 0..3FFFh Double-protected (when BIOSPROT is set)
[BIOSPROT]..3FFFh [BIOSPROT]..3FFFh Normal-protected (always active)
The initial BIOSPROT setting on power-up is zero (disabled). Before starting the cartridge, the BIOS boot code sets the register to 1204h (actually 1205h, but the mis-aligned low-bit is ignored). Once when initialized, further writes to the register are ignored.
The double-protected region contains the exception vectors, some bytes of code, and the cartridge KEY1 encryption seed (about 4KBytes). As far as I know, it is impossible to unlock the memory once when it is locked, however, with some trickery, it is possible execute code before it gets locked. Also, the two THUMB opcodes at 05ECh can be used to read all memory at 0..3FFFh,
05ECh ldrb r3,[r3,12h] ;requires incoming r3=src-12h
05EEh pop r2,r4,r6,r7,r15 ;requires dummy values & THUMB retadr on stack
Additionally most BIOS functions (eg. CpuSet), include a software-based protection which rejects source addresses in the BIOS area (the only exception is GetCRC16, though it still cannot bypass the BIOSPROT setting).
The NDS9 BIOS doesn’t include any software or hardware based read protection.
Bus clock = 33MHz (33.513982 MHz) (1FF61FEh Hertz)
NDS7 clock = 33MHz (same as bus clock)
NDS9 clock = 66MHz (internally twice bus clock; for cache/tcm)
Most timings in this document are specified for 33MHz clock (not for the 66MHz clock). Respectively, NDS9 timings are counted in “half” cycles.
Tables below show the different access times for code/data fetches on arm7/arm9 cpus, measured for sequential/nonsequential 32bit/16bit accesses.
NDS7/CODE NDS9/CODE
N32 S32 N16 S16 Bus N32 S32 N16 S16 Bus
9 2 8 1 16 9 9 4.5 4.5 16 Main RAM (read) (cache off)
1 1 1 1 32 4 4 2 2 32 WRAM,BIOS,I/O,OAM
2 2 1 1 16 5 5 2.5 2.5 16 VRAM,Palette RAM
16 12 10 6 16 19 19 9.5 9.5 16 GBA ROM (example 10,6 access)
- - - - - 0.5 0.5 0.5 0.5 32 TCM, Cache_Hit
- - - - - (--Load 8 words--) Cache_Miss
NDS7/DATA NDS9/DATA
N32 S32 N16 S16 Bus N32 S32 N16 S16 Bus
10 2 9 1 16 10 2 9 1 16 Main RAM (read) (cache off)
1 1 1 1 32 4 1 4 1 32 WRAM,BIOS,I/O,OAM
1? 2 1 1 16 5 2 4 1 16 VRAM,Palette RAM
15 12 9 6 16 19 12 13 6 16 GBA ROM (example 10,6 access)
9 10 9 10 8 13 10 13 10 8 GBA RAM (example 10 access)
- - - - - 0.5 0.5 0.5 - 32 TCM, Cache_Hit
- - - - - (--Load 8 words--) Cache_Miss
- - - - - 11 11 11 - 32 Cache_Miss (BIOS)
- - - - - 23 23 23 - 16 Cache_Miss (Main RAM)
All timings are counted in 33MHz units (so “half” cycles can occur on NDS9).
Note: 8bit data accesses have same timings than 16bit data.
*** DS Memory Timing Notes ***
The NDS timings are altogether pretty messed up, with different timings for CODE and DATA fetches, and different timings for NDS7 and NDS9…
Timings for this region can be considered as “should be” timings.
Quite the same as NDS7/CODE. Except that, nonsequential Main RAM accesses are 1 cycle slower, and more strange, nonsequential GBA Slot accesses are 1 cycle faster.
This is the most messiest timing. An infamous PENALTY of 3 cycles is added to all nonsequential accesses (except cache, tcm, and main ram). And, all opcode fetches are forcefully made nonsequential 32bit (the NDS9 simply doesn’t support fast sequential opcode fetches). That applies also for THUMB code (two 16bit opcodes are fetched by a single nonsequential 32bit access) (so the time per 16bit opcode is one half of the 32bit fetch) (unless a branch causes only one of the two 16bit opcodes to be executed, then that opcode will have the full 32bit access time).
Allows both sequential and nonsequential access, and both 16bit and 32bit access, so it’s faster than NDS9/CODE. Nethertheless, it’s still having the 3 cycle PENALTY on nonsequential accesses. And, similar as NDS7/DATA, it’s also adding 1 cycle to nonsequential Main RAM accesses.
*** More Timing Notes / Lots of unsorted Info ***
The 33MHz NDS7 is running more or less nicely at 33MHz. However, the so-called “66MHz” NDS9 is having <much> higher waitstates, and it’s effective bus speed is barely about 8..16MHz, the only exception is code/data in cache/tcm, which is eventually reaching real 66MHz (that, assuming cache HITS, otherwise, in case of cache MISSES, the cached memory timing might even drop to 1.4MHz or so?).
*********************
ARM9 opcode fetches are always N32 + 3 waits.
S16 and N16 do not exist (because thumb-double-fetching) (see there).
S32 becomes N32 (ie. the ARM9 does NOT support fast sequential timing).
That N32 is having same timing as normal N32 access on NDS7, plus 3 waits.
Eg. an ARM9 N32 or S32 to 16bit bus will take: N16 + S16 + 3 waits.
Eg. an ARM9 N32 or S32 to 32bit bus will take: N32 + 3 waits.
Main Memory is ALWAYS having the nonsequential 3 wait PENALTY (even on ARM7).
*********************
ARM9 Data fetches however are allowed to use sequential timing, as well as raw 16bit accesses (which aren’t forcefully expanded to slow 32bit accesses).
Nethertheless, the 3 wait PENALTY is added to any NONSEQUENTIAL accesses.
Only exceptions are cache and tcm which do not have that penalty.
Eventually, data fetches can take place parallel with opcode fetches.
*********************
Unlike ARM7, the ARM9 has separate code and data busses, allowing it to perform code and data fetches simultaneously (provided that both are in different memory regions).
Normally, opcode execution times are calculated as “(codetime+datatime)”, with the two busses, it can (ideally) be “MAX(codetime,datatime)”, so the data access time may virtually take “NULL” clock cycles.
In practice, DTCM and Data Cache access can take NULL cycles (however, data access to ITCM can’t).
When executing code in cache/itcm, data access to non-cache/tcm won’t be any faster than with only one bus (as it’s best, it could subtract 0.5 cycles from datatime, but, the access must be “aligned” to the bus-clock, so the “datatime-0.5” will be rounded back to the original “datatime”).
When executing code in uncached main ram, and accessing data (elsewhere than in main memory, cache/tcm), then execution time is typically “codetime+datatime-2”.
Additionally to codetime+datatime, some opcodes include one or more internal cycles. Compared with ARM7, the behaviour of that internal cycles is slightly different on ARM9. First of, on the NDS9, the internal cycles are of course “half” cycles (ie. counted in 66MHz units, not in 33MHz units) (although they may get rounded to “full” cycles upon next memory access outside tcm/cache). And, the ARM9 is in some cases “skipping” the internal cycles, that often depending on whether or not the next opcode is using the result of the current opcode.
Another big difference is that the ARM9 has lost the fast-multiply feature for small numbers; in some cases that may result in faster execution, but may also result in slower execution (one workaround would be to manually replace MUL opcodes by the new ARM9 halfword multiply opcodes); the slowest case are MUL opcodes that do update flags (eg. MULS, MLAS, SMULLS, etc. in ARM mode, and all ALL multiply opcodes in THUMB mode).
In thumb mode, the NDS9 is fetching two 16bit opcodes by a single 32bit read. In case of 32bit bus, this reduces the amount of memory traffic and may result in faster execution time, of course that works only if the two opcodes are within a word-aligned region (eg. loops at word-aligned addresses will be faster than non-aligned loops). However, the double-opcode-fetching is also done on 16bit bus memory, including for unnecessary fetches, such like opcodes after branch commands, so the feature may cause heavy slowdowns.
Reportedly, the main memory access times would be 5 cycles (nonsequential read), 4 cycles (nonsequential write), and 1 cycle (sequential read or write). Plus whatever termination cycles. Plus 3 cycles on nonsequential access to the last 2-bytes of a 32-byte block.
That’s of course all wrong. Reads are much slower than 5 cycles. Not yet tested if writes are faster. And, I haven’t been able to reproduce the 3 cycles on last 2-bytes effect, actually, it looks more as if that 3 cycles are accidently added to ALL nonsequential accesses, at ALL main memory addresses, and even to most OTHER memory regions… which might be the source of the PENALTY which occurs on VRAM/WRAM/OAM/Palette and I/O accesses.
In some cases DMA main memory read cycles are reportedly performed simultaneously with DMA write cycles to other memory.
On the NDS9, all external memory access (and I/O) is delayed to bus clock (or actually MUCH slower due to the massive waitstates), so the full 66MHz can be used only internally in the NDS9 CPU core, ie. with cache and TCM.
The exact bus clock is specified as 33.513982 MHz (1FF61FEh Hertz). However, on my own NDS, measured in relation to the RTC seconds IRQ, it appears more like 1FF6231h, that inaccuary of 1 cycle per 657138 cycles (about one second per week) on either oscillator, isn’t too significant though.
The access time for GBA slot can be configured via EXMEMCNT register.
Additionally, on NDS9, a one cycle wait can be added to VRAM accesses (when the video controller simultaneously accesses it) (that can be disabled by Forced Blank, see DISPCNT.Bit7). Moreover, additional VRAM waitstates occur when using the video capture function.
Note: VRAM being mapped to NDS7 is always free of additional waits.
The NDS has two 2D Video Engines, each basically the same as in GBA, see
For Display Power Control (and Display Swap), and VRAM Allocation, see
Dot clock = 5.585664 MHz (=33.513982 MHz / 6)
H-Timing: 256 dots visible, 99 dots blanking, 355 dots total (15.7343KHz)
V-Timing: 192 lines visible, 71 lines blanking, 263 lines total (59.8261 Hz)
The V-Blank cycle for the 3D Engine consists of the 23 lines, 191..213.
Screen size 62.5mm x 47.0mm (each) (256x192 pixels)
Vertical space between screens 22mm (equivalent to 90 pixels)
0-4 Factor used for 6bit R,G,B Intensities (0-16, values >16 same as 16)
Brightness up: New = Old + (63-Old) * Factor/16
Brightness down: New = Old - Old * Factor/16
5-13 Not used
14-15 Mode (0=Disable, 1=Up, 2=Down, 3=Reserved)
16-31 Not used
The LY and LYC values are in range 0..262, so LY/LYC values have been expanded to 9bit values: LY = VCOUNT Bit 0..8, and LYC=DISPSTAT Bit8..15,7.
VCOUNT register is write-able, allowing to synchronize linked DS consoles.
For proper synchronization:
write new LY values only in range of 202..212
write only while old LY values are in range of 202..212
DISPSTAT/VCOUNT supported by NDS9 (Engine A Ports, without separate Engine B Ports), and by NDS7 (allowing to synchronize NDS7 with display timings).
Similar as on GBA, the VBlank flag isn’t set in the last line (ie. only in lines 192..261, but not in line 262).
Although the drawing time is only 1536 cycles (256*6), the NDS9 H-Blank flag is “0” for a total of 1606 cycles (and, for whatever reason, a bit longer, 1613 cycles in total, on NDS7).
The display controller performs VRAM-reads once every 6 clock cycles, a 1 cycle waitstate is generated if the CPU simultaneously accesses VRAM. With capture enabled, additionally VRAM-writes take place once every 6 cycles, so the total VRAM-read/write access rate is then once every 3 cycles.
The DS counts scanlines in range 0..262 (0..106h), of which only the lower 8bit are compared with the WIN0V/WIN1V register settings. Respectively, Y1 coordinates 00h..06h will be triggered in scanlines 100h-106h by mistake. That means, the window gets activated within VBlank period, and will be active in scanline 0 and up (that is no problem with Y1=0, but Y1=1..6 will appear as if if Y1 would be 0). Workaround would be to disable the Window during VBlank, or to change Y1 during VBlank (to a value that does not occur during VBlank period, ie. 7..191).
Also, there’s a problem to fit the 256 pixel horizontal screen resolution into 8bit values: X1=00h is treated as 0 (left-most), X2=00h is treated as 100h (right-most). However, the window is not displayed if X1=X2=00h; the window width can be max 255 pixels.
Includes two 2D Engines, called A and B. Both engines are accessed by the ARM9 processor, each using different memory and register addresses:
Region______Engine A______________Engine B___________
I/O Ports 4000000h 4001000h
Palette 5000000h (1K) 5000400h (1K)
BG VRAM 6000000h (max 512K) 6200000h (max 128K)
OBJ VRAM 6400000h (max 256K) 6600000h (max 128K)
OAM 7000000h (1K) 7000400h (1K)
Engine A additionally supports 3D and large-screen 256-color Bitmaps, plus main-memory-display and vram-display modes, plus capture unit.
The LCD screens are best viewed at viewing angles of 90 degrees. Colors may appear distorted, and may even become invisible at other viewing angles.
When the console is handheld, both screens can be turned into preferred direction. When the console is settled on a table, only the upper screen can be turned, but the lower screen is stuck into horizontal position - which results in rather bad visibility (unless the user moves his/her head directly above of it).
Bit0-3 "COMMAND" (?)
Bit4-7 "COMMAND2" (?)
Bit8-11 "COMMAND3" (?)
This register has been mentioned in an early I/O map from Nintendo, as far as I know, the register isn’t used by any games/firmware/bios, not sure if it does really exist on release-version, or if it’s been prototype stuff…?
The screens in the DS-Lite seem to allow a wider range of vertical angles.
The bad news is that the colors of the DS-Lite are (no surprise) not backwards compatible with older NDS and GBA displays. The good news is that Nintendo has finally reached near-CRT-quality (without blurred colors), so one could hope that they won’t show up with more displays with other colors in future.
Don’t know if there’s an official/recommended way to detect DS-Lite displays (?) possible methods would be whatever values in Firmware header, or by functionality of Power Managment device, or (not too LCD-related) by Wifi Chip ID.
Bit Engine Expl.
0-2 A+B BG Mode
3 A BG0 2D/3D Selection (instead CGB Mode) (0=2D, 1=3D)
4 A+B Tile OBJ Mapping (0=2D; max 32KB, 1=1D; max 32KB..256KB)
5 A+B Bitmap OBJ 2D-Dimension (0=128x512 dots, 1=256x256 dots)
6 A+B Bitmap OBJ Mapping (0=2D; max 128KB, 1=1D; max 128KB..256KB)
7-15 A+B Same as GBA
16-17 A+B Display Mode (Engine A: 0..3, Engine B: 0..1, GBA: Green Swap)
18-19 A VRAM block (0..3=VRAM A..D) (For Capture & above Display Mode=2)
20-21 A+B Tile OBJ 1D-Boundary (see Bit4)
22 A Bitmap OBJ 1D-Boundary (see Bit5-6)
23 A+B OBJ Processing during H-Blank (was located in Bit5 on GBA)
24-26 A Character Base (in 64K steps) (merged with 16K step in BGxCNT)
27-29 A Screen Base (in 64K steps) (merged with 2K step in BGxCNT)
30 A+B BG Extended Palettes (0=Disable, 1=Enable)
31 A+B OBJ Extended Palettes (0=Disable, 1=Enable)
Engine A BG Mode (DISPCNT LSBs) (0-6, 7=Reserved)
Mode BG0 BG1 BG2 BG3
0 Text/3D Text Text Text
1 Text/3D Text Text Affine
2 Text/3D Text Affine Affine
3 Text/3D Text Text Extended
4 Text/3D Text Affine Extended
5 Text/3D Text Extended Extended
6 3D - Large -
Of which, the “Extended” modes are sub-selected by BGxCNT bits:
BGxCNT.Bit7 BGxCNT.Bit2 Extended Affine Mode Selection
0 CharBaseLsb rot/scal with 16bit bgmap entries (Text+Affine mixup)
1 0 rot/scal 256 color bitmap
1 1 rot/scal direct color bitmap
Engine B: Same as above, except that: Mode 6 is reserved (no Large screen bitmap), and BG0 is always Text (no 3D support).
Affine = formerly Rot/Scal mode (with 8bit BG Map entries)
Large Screen Bitmap = rot/scal 256 color bitmap (using all 512K of 2D VRAM)
0 Display off (screen becomes white)
1 Graphics Display (normal BG and OBJ layers)
2 Engine A only: VRAM Display (Bitmap from block selected in DISPCNT.18-19)
3 Engine A only: Main Memory Display (Bitmap DMA transfer from Main RAM)
Mode 2-3 display a raw direct color bitmap (15bit RGB values, the upper bit in each halfword is unused), without any further BG,OBJ,3D layers, these modes are completely bypassing the 2D/3D engines as well as any 2D effects, however the Master Brightness effect can be applied to these modes. Mode 2 is particulary useful to display captured 2D/3D images (in that case it can indirectly use the 2D/3D engine).
character base extended from bit2-3 to bit2-5 (bit4-5 formerly unused)
engine A screen base: BGxCNT.bits*2K + DISPCNT.bits*64K
engine B screen base: BGxCNT.bits*2K + 0
engine A char base: BGxCNT.bits*16K + DISPCNT.bits*64K
engine B char base: BGxCNT.bits*16K + 0
char base is used only in tile/map modes (not bitmap modes)
screen base is used in tile/map modes,
screen base used in bitmap modes as BGxCNT.bits*16K, without DISPCNT.bits*64K
screen base however NOT used at all for Large screen bitmap mode
bgcnt size text rotscal bitmap large bmp
0 256x256 128x128 128x128 512x1024
1 512x256 256x256 256x256 1024x512
2 256x512 512x512 512x256 -
3 512x512 1024x1024 512x512 -
bitmaps that require more than 128K VRAM are supported on engine A only.
For BGxCNT.Bit7 and BGxCNT.Bit2 in Extended Affine modes, see above BG Mode description (extended affine doesn’t include 16-color modes, so color depth bit can be used for mode selection. Also, bitmap modes do not use charbase, so charbase.0 can be used for mode selection as well).
for BG0CNT, BG1CNT only: bit13 selects extended palette slot
(BG0: 0=Slot0, 1=Slot2, BG1: 0=Slot1, 1=Slot3)
Direct Color Bitmap BG, and Direct Color Bitmap OBJ
BG/OBJ Supports 32K colors (15bit RGB value) - so far same as GBAs BG.
However, the upper bit (Bit15) is used as Alpha flag. That is, Alpha=0=Transparent, Alpha=1=Normal (ie. on the NDS, Direct Color values 0..7FFFh are NOT displayed).
Unlike GBA bitmap modes, NDS bitmap modes are supporting the Area Overflow bit (BG2CNT and BG3CNT, Bit 13).
The GBA has been assigning OBJ priority in respect to the 7bit OAM entry number, regardless of the OBJs 2bit BG-priority attribute (which allowed to specify invalid priority orders). That problem has been fixed in DS mode by combining the above two values into a 9bit priority value.
Bit4 Bit20-21 Dimension Boundary Total ;Notes
0 x 2D 32 32K ;Same as GBA 2D Mapping
1 0 1D 32 32K ;Same as GBA 1D Mapping
1 1 1D 64 64K
1 2 1D 128 128K
1 3 1D 256 256K ;Engine B: 128K max
TileVramAddress = TileNumber * BoundaryValue
Even if the boundary gets changed, OBJs are kept composed of 8x8 tiles.
Bitmap OBJs are 15bit Direct Color data, plus 1bit Alpha flag (in bit15).
Bit6 Bit5 Bit22 Dimension Boundary Total ;Notes
0 0 x 2D/128 dots 8x8 dots 128K ;Source Bitmap width 128 dots
0 1 x 2D/256 dots 8x8 dots 128K ;Source Bitmap width 256 dots
1 0 0 1D 128 bytes 128K ;Source Width = Target Width
1 0 1 1D 256 bytes 256K ;Engine A only
1 1 x Reserved
In 1D mapping mode, the Tile Number is simply multiplied by the boundary value.
1D_BitmapVramAddress = TileNumber(0..3FFh) * BoundaryValue(128..256)
2D_BitmapVramAddress = (TileNo AND MaskX)*10h + (TileNo AND NOT MaskX)*80h
In 2D mode, the Tile Number is split into X and Y indices, the X index is located in the LSBs (ie. MaskX=0Fh, or MaskX=1Fh, depending on DISPCNT.5).
Setting the OBJ Mode bits (Attr 0, Bit10-11) to a value of 3 has been prohibited in GBA, however, in NDS it selects the new Bitmap OBJ mode; in that mode, the Color depth bit (Attr 0, Bit13) should be set to zero; also in that mode, the color bits (Attr 2, Bit 12-15) are used as Alpha-OAM value (instead of as palette setting).
On the GBA, a large OBJ (with 64pix height, scaled into double-size region of 128pix height) located near the bottom of the screen has been wrapped to the top of the screen (and was NOT displayed at the bottom of the screen).
This problem has been “corrected” in the NDS (except in GBA mode), that is, on the NDS, the OBJ appears BOTH at the top and bottom of the screen. That isn’t necessarily better - the advantage is that one can manually enable/disable the OBJ in the desired screen-half on IRQ level; that’d be required only if the wrapped portion is non-transparent.
When allocating extended palettes, the allocated memory is not mapped to the CPU bus, so the CPU can access extended palette only when temporarily de-allocating it.
Color 0 of all standard/extended palettes is transparent, color 0 of BG standard palette 0 is used as backdrop. extended palette memory must be allocated to VRAM.
BG Extended Palette enabled in DISPCNT Bit 30, when enabled,
Allocated VRAM is split into 4 slots of 8K each (32K used in total), normally BG0..3 are using Slot 0..3, however BG0 and BG1 can be optionally changed to BG0=Slot2, and BG1=Slot3 via BG0CNT and BG1CNT.
OBJ Extended Palette enabled in DISPCNT Bit 31, when enabled,
Extended OBJ palette memory must be allocated to VRAM F, G, or I (which are 16K) of which only the first 8K are used for extended palettes (=1000h 16bit entries).
Capture is supported for Display Engine A only.
0-4 EVA (0..16 = Blending Factor for Source A)
5-7 Not used
8-12 EVB (0..16 = Blending Factor for Source B)
13-15 Not used
16-17 VRAM Write Block (0..3 = VRAM A..D) (VRAM must be allocated to LCDC)
18-19 VRAM Write Offset (0=00000h, 0=08000h, 0=10000h, 0=18000h)
20-21 Capture Size (0=128x128, 1=256x64, 2=256x128, 3=256x192 dots)
22-23 Not used
24 Source A (0=Graphics Screen BG+3D+OBJ, 1=3D Screen)
25 Source B (0=VRAM, 1=Main Memory Display FIFO)
26-27 VRAM Read Offset (0=00000h, 0=08000h, 0=10000h, 0=18000h)
28 Not used
29-30 Capture Source (0=Source A, 1=Source B, 2/3=Sources A+B blended)
31 Capture Enable (0=Disable/Ready, 1=Enable/Busy)
Notes:
VRAM Read Block (VRAM A..D) is selected in DISPCNT Bits 18-19.
VRAM Read Block can be (or must be ?) allocated to LCDC (MST=0).
VRAM Read Offset is ignored (zero) in VRAM Display Mode (DISPCNT.16-17).
VRAM Read/Write Offsets wrap to 00000h when exceeding 1FFFFh (max 128K).
Capture Sizes less than 256x192 capture the upper-left portion of the screen.
Blending factors EVA and EVB are used only if “Source A+B blended” selected.
After setting the Capture Enable bit, capture starts at next line 0, and the capture enable/busy bit is then automatically cleared (in line 192, regardless of the capture size).
Capture data is 15bit color depth (even when capturing 18bit 3D-images).
Capture A: Dest_Intensity = SrcA_Intensitity ; Dest_Alpha=SrcA_Alpha.
Capture B: Dest_Intensity = SrcB_Intensitity ; Dest_Alpha=SrcB_Alpha.
Capture A+B (blending):
Dest_Intensity = ( (SrcA_Intensitity * SrcA_Alpha * EVA)
+ (SrcB_Intensitity * SrcB_Alpha * EVB) ) / 16
Dest_Alpha = (SrcA_Alpha AND (EVA>0)) OR (SrcB_Alpha AND EVB>0))
Capture provides a couple of interesting effects.
For example, 3D Engine output can be captured via source A (to LCDC-allocated VRAM), in the next frame, either Graphics Engine A or B can display the captured 3D image in VRAM image as BG2, BG3, or OBJ (from BG/OBJ-allocated VRAM); this method requires to switch between LCDC- and BG/OBJ-allocation.
Another example would be to capture Engine A output, the captured image can be displayed (via VRAM Display mode) in the following frames, simultaneously the new Engine A output can be captured, blended with the old captured image; in that mode moved objects will leave traces on the screen; this method works with a single LCDC-allocated VRAM block.
Intended to send 256x192 pixel 32K color bitmaps by DMA directly
- to Screen A (set DISPCNT to Main Memory Display mode), or
- to Display Capture unit (set DISPCAPCNT to Main Memory Source).
The FIFO can receive 4 words (8 pixels) at a time, each pixel is a 15bit RGB value (the upper bit, bit15, is unused).
Set DMA to Main Memory mode, 32bit transfer width, word count set to 4, destination address to DISP_MMEM_FIFO, source address must be in Main Memory.
Transfer starts at next frame.
Main Memory Display/Capture is supported for Display Engine A only.
_____________ __________
VRAM A -->| 2D Graphics |--------OBJ->| |
VRAM B -->| Engine A |--------BG3->| Layering |
VRAM C -->| |--------BG2->| and |
VRAM D -->| |--------BG1->| Special |
VRAM E -->| | ___ | Effects |
VRAM F -->| |->|SEL| | | ______
VRAM G -->| - - - - - - | |BG0|-BG0->| |----o--->| |
| 3D Graphics |->|___| |__________| | |Select|
| Engine | | |Video |
|_____________|--------3D----------------. | |Input |
_______ _______ ___ | | | |
| | | |<-----------|SEL|<-' | |and |-->
| | | | _____ |A | | | |
VRAM A <--|Select | |Select | | |<-|___|<----' |Master|
VRAM B <--|Capture|<---|Capture|<--|Blend| ___ |Bright|
VRAM C <--|Dest. | |Source | |_____|<-|SEL|<----. |A |
VRAM D <--| | | | |B | | | |
|_______| |_______|<-----------|___|<-. | | |
_______ | | | |
VRAM A -->|Select | | | | |
VRAM B -->|Display|--------------------------------o------>| |
VRAM C -->|VRAM | | | |
VRAM D -->|_______| _____________ | | |
|Main Memory | | | |
Main ------DMA---->|Display FIFO |------------------o--->|______|
Memory |_____________|
_____________ __________ ______
VRAM C -->| 2D Graphics |--------OBJ->| Layering | | |
VRAM D -->| Engine B |--------BG3->| and | |Master|
VRAM H -->| |--------BG2->| Special |-------->|Bright|-->
VRAM I -->| |--------BG1->| Effects | |B |
|_____________|--------BG0->|__________| |______|
eg. used in DSi Launcher “rom:\layout\cmn\launcher_d.szs..”
____________________________ Nitro Color Palette _____________________________
000h 4 Chunk ID "RLCN" (aka NCLR backwards, Nitro Color Resource)
004h 2 Byte Order (FEFFh)
006h 2 Version (0100h)
008h 4 Total Filesize
00Ch 2 Offset to "TTLP" Chunk, aka Size of "RLCN" Chunk (0010h)
00Eh 2 Total number of following Chunks (1=TTLP) (or 2=TTLP+PMCP ?)
000h 4 Chunk ID "TTLP" (aka PLTT backwards, Palette data)
004h 4 Chunk Size (eg. 0218h)
008h 4 Reportedly Color Depth (ie. "tile usage info") (3=4bpp, 4=8bpp)
00Ch 4 Zero
010h 4 Palette Data Size in bytes (eg. 200h) (or 200h-N? no, blah!)
014h 4 Offset from TTLP+8 to Palette Data? (always 10h)
018h N*2 Palete Data (16bit colors, 0000h..7FFFh)
Most DSi titles use full 200h-byte palettes (Paper Plane has a smaller one in Graphics.NARC\Seq\pause.zcl). There seem to be no DSi titles with PMCP chunks.
000h 4 Chunk ID "PMCP" (aka PCMP backwards, Palette CMP?)
004h 4 Chunk Size (reportedly always 12h ???)
008h 2 Number of palettes in file (uh?)
00Ah 2 Unused (BEEFh=Bullshit)
00Ch 4 Offset from PMCP+8 to Palette IDs? (always 08h)
DATA N*2 "Palette ID numbers for each palette (starting from 0)"
___________________________ Nitro Character Tiles ____________________________
eg. DSi Launcher "rom:\debug\DebugFont.NCGR" -- with SOPC chunk
eg. DSi Launcher "rom:\layout\cmn\launcher_d.szs\.." -- without SOPC chunk
000h 4 Chunk ID "RGCN" (aka NCGR backwards, Nitro Char Graphics Resource)
004h 2 Byte Order (FEFFh)
006h 2 Version (0101h) (unknown if 0100h does also exist?)
008h 4 Total Filesize
00Ch 2 Offset to "RAHC" Chunk, aka Size of "RGCN" Chunk (0010h)
00Eh 2 Total number of following Chunks (1=RAHC, or 2=RAHC+SOPC)
000h 4 Chunk ID "RAHC" (aka CHAR backwards)
004h 4 Chunk Size (eg. 1420h)
008h 2 Tile Data Size in Kilobytes ;\or both set to FFFFh
00Ah 2 Unknown (always 20h) ;/(when size<>N*1024)
00Ch 4 Color Depth (3=4bpp, 4=8bpp)
010h 2 Zero ;or 10h (when SOPC not exists? kbyte size rounded up?)
012h 2 Zero ;or 20h (when SOPC not exists?)
014h 4 Zero
018h 4 Tile Data Size in Bytes (eg. 1400h)
01Ch 4 Offset from RAHC+8 to Tile Data? ;=always 18h
020h ... Tile Data (eg. 20h-byte zerofilled for 4bpp SPC char?)
Nonzero [10h,12h] spotted in Paper Plane “rom:\Graphics.NARC\Plane\plane.zcg”.
000h 4 Chunk ID "SOPC" (aka CPOS backwards)
004h 4 Chunk Size (10h)
008h 4 Zero
00Ch 2 Same as [00Ah] in RAHC chunk? (always 20h)
00Eh 2 Same as [008h] in RAHC chunk? (size in kilobytes)
__________________________ Unknown Character Tiles ___________________________
Apart from above NCGR, there is reportedly another tile format:
NCGR (Nitro Character Graphic Resource) - Graphical Tiles --> see above
NBGR (Nitro Basic Graphic Resource) - Graphical Tiles --> what ???
If it does really exist for real… the header ID be “RGBN” (aka NBGR backwards), and file extension might be NBGR? But even if so, it’s unknown if/when/where/why that NBGR format is used. If the “B” is for “Basic” then might have less features than NCGR, or maybe it might be “B” for non-tiled Bitmaps, or whatever?
___________________________ Nitro BG Maps Screens ____________________________
000h 4 Chunk ID "RCSN" (aka NSCR backwards, Nitro Screen Resource)
004h 2 Byte Order (FEFFh)
006h 2 Version (0100h)
008h 4 Total Filesize
00Ch 2 Offset to "NRCS" Chunk, aka Size of "RCSN" Chunk (0010h)
00Eh 2 Total number of following Chunks (1=NRCS)
000h 4 Chunk ID "NRCS" (aka SCRN backwards, Screen)
004h 4 Chunk Size
008h 4 Screen Width in pixels
00Ah 2 Screen Height in pixels
00Ch 4 Zero
010h 4 Screen Data Size (width/8)*(height/8)*2
014h N*2 Screen Data (16bit BG Map entries, palette+xyflip+tileno)
____________________________ Nitro OBJ Animations ____________________________
000h 4 Chunk ID "RNAN" (aka NANR backwards, Nitro Animation Resource)
004h 2 Byte Order (FEFFh)
006h 2 Version (0100h)
008h 4 Total Filesize
00Ch 2 Offset to "KNBA" Chunk, aka Size of "RNAN" Chunk (0010h)
00Eh 2 Total number of following Chunks (1=KNBA, or 3=KNBA+LBAL+TXEU)
One chunk exists in DSi Launcher
Three chunks exist in DSi Flipnote “rom:ManualData.Eu\md2res_narc.blz\data\obj
000h 4 Chunk ID "KNBA" (aka ABNK backwards, Animation Bank)
004h 4 Chunk Size (always padded to 4-byte boundary if LABL chunk follows)
008h 2 Number of 16-byte Animation Blocks ;implies NumLabels in LABL chunk
00Ah 2 Number of 8-byte Frame Blocks
00Ch 4 Offset from KNBA+8 to Animation Blocks ;=18h
010h 4 Offset from KNBA+8 to Frame Blocks ;=[0Ch]+[08h]*10h
014h 4 Offset from KNBA+8 to Frame Data ;=[10h]+[0Ah]*8
018h 8 Zero
DATA .. Animation Blocks (16-byte entries)
00h 4 Number of Frames
04h 2 Unknown (0)
06h 2 Unknown Always (1) ;reportedly "always unknown"
08h 4 Unknown (1..2)
0Ch 4 Offset from FrameBlock+0 to First Frame
DATA .. Frame Blocks (8-byte entries)
00h 4 Offset from FrameData+0 to whatever? (always 4-byte aligned?)
04h 2 Frame Width ;3Ch or 01..06h ;Time in 60Hz units? num meta's?
06h 2 Unused (usually 0000h, or BEEFh=Bullshit)
DATA .. Frame Data (2-byte entries)
00h 2 Unknown 16bit values? (maybe CELL index or whatever??) (CCCCh=?)
000h 4 Chunk ID "LBAL" (aka LABL backwards, Labels)
004h 4 Chunk Size (not padded to 4-byte size, following TXEU is unaligned)
008h 4*N Offsets from LabelArea+0 to Labels (for each Animation Block)
... .. Label Area (ASCII Strings, terminated by 00h)
The LabelArea starts at LBAL+8+NumLabels*4 (whereas, NumLabels is found in KNBA chunk).
Caution: Not 4-byte aligned (the preceeding LBAL chunk can have odd size).
000h 4 Chunk ID "TXEU" (aka UEXT backwards, Whatever Extension or so?)
004h 4 Chunk Size (0Ch)
008h 4 Unknown (usually 0) (reportedly 0 or 1)
__________________________ Nitro OBJ Metatile Cells __________________________
000h 4 Chunk ID "RECN" (aka NCER backwards, Nitro Cell Resource)
004h 2 Byte Order (FEFFh)
006h 2 Version (0100h)
008h 4 Total Filesize
00Ch 2 Offset to "KBEC" Chunk, aka Size of "RECN" Chunk (0010h)
00Eh 2 Total number of following Chunks (1=KBEC, or 3=KBEC+LBAL+TXEU)
000h 4 Chunk ID "KBEC" (aka CEBK backwards, Cell Bank)
004h 4 Chunk Size (always padded to 4-byte boundary if LABL chunk follows)
008h 2 Number of Metatiles
00Ah 2 Metatiles Entry Size (0=Normal 8 bytes, 1=Extended 16 bytes)
(DSi Launcher ..layout\cmn\launcher_u\.. uses 16-byte size)
00Ch 4 Offset from KBEC+8 to Metatile Table? (18h)
010h 4 Boundary Size (?) (but is ZERO in layout\cmn\launcher_u\)
"Specifies the area in which the image can be drawn,
multiplied by 64, ie. 2 means that the area is 128x128 pixels."
014h 0Ch Zero
020h .. Metatile Table (8 bytes each) (or 16 bytes)
... .. OBJ Attribute Table (6-bytes each)
Metatile Table entries are (8-byte or 16-byte):
000h 2 Number of OBJs
002h 2 Unknown
004h 4 OBJ Data Offset (from begin of OBJ Attr Table)
(008h 2) Unknown (can be 02h,10h,48h,74h)
(00Ah 2) Unknown (can be 08h)
(00Ch 2) Unknown (can be FFA0h..FFF0h) ;\maybe extra coordinate offsets?
(00Eh 2) Unknown (can be FFF0h..FFF9h) ;/
OBJ Attribute Table…
starts at Number of Cells * 8 | each cell is made up of 6 bytes)
The 6-byte OBJ Attributes seem to be in normal OAM format, containing the coordinates, tile number, tile size, and other flags (however, the coordinates contain signed values; ie. one needs to add the current OBJ position to those values). For details on the OBJ Attributes, see:
Same as in Animation files (see there). In fact, the content seems to be SAME as the corresponding Animation file (for pairs of filename.NANR and filename.NCER), and the number of labels must be obtained from the NANR file’s KNBA chunk (as such, it’s rather useless to have LBAL in NCER files, except perhaps for error checking that the correct file pair was loaded).
Note: DSi Launcher layout\cmn\logodemo.szs has KBEC Chunk Size 2B6h (although the filesize is padded as if it were 2B8h bytes) (the file has no LBAL chunk, so it’s unclear if/how it were aligned if present).
____________________________ Nitro Unknown Files ____________________________
DSi Deep Psyche has two extra file types:
.NMAR file (with "RAMN" header ID, and "KNBA"+"LBAL" chunks)
.NMCR file (with "RCMN" header ID, and "KBCM" chunk)
The purpose is unknown, but they are probably also animating something…
OBJ with 16bit x/y (instead 9bit/8bit)?
OBJ with fractional x/y-stepping (moving/motion)?
OBJ rotation/scaling?
BG scroll offsets?
BG tile replacement?
Going by the filename (a01_obj01.NMAR) they seem to be OBJ related. Labels include things like “a01_upNN” and “a01_windowNN”.
The chunks seem to resemble those in RNAN/RECN files (Animation+Cells). Except, the KBCM has 8-byte entries (unlike the 6-byte ones in KBEC):
000h 2 Unknown (000xh..007Ah, maybe time or so?)
002h 2 Unknown (signed 16bit?)
004h 2 Unknown (signed 16bit?)
006h 2 Unknown (0x21h, with x=0..8)
_________________________ Nitro More Unknown Files __________________________
Some 2D folders contain more unknown files (eg. DSi Camera “rom:layout\cmn\fusion_camera.szs”):
000h 4 ID "JNBL"
004h 2 Zero
006h 2 Number of 6-byte entries (01h or more)
008h N*6 Unknown
000h 4 ID "JNCL"
004h 2 Zero (0000h)
006h 2 Number of 8-byte entries (01h or more)
008h N*8 Unknown (eg. 80h,10h,C0h,20h,00h,00h,00h,00h)
000h 4 ID "JNLL"
004h 2 Zero (0000h)
006h 2 Number of 16-byte entries (01h or more)
008h N*16 Unknown (eg. 80h,50h,60h,10h,7Ch,29h,FFh,FDh,0Dh,19h,0,0,0,0,0,0)
000h 4 ID "BNGL" ;this same as file extension (not JNGL)
004h 2 Zero (0000h)
006h 2 Number of ?-byte entries (01h or more)
008h 2 Unknown (can be 02h,04h,06h,0Ah)
00Ah 2 Number of ?-byte other entries maybe (01h or more)
...
... Entries?
... Other Entries?
... Maybe More Other Entries?
Filesize can range from 18h bytes to 24Ah bytes (or maybe yet smaller/biggger).
______________________________ .ntft and .ntfp _______________________________
Probably texture data (maybe for use as extra 2D layer), size is usually/always a power of 2 (ranging from 80h bytes to at least 64Kbytes (or even 512Kbytes?). Color depth can be 16bit or 8bit (and maybe less). Files with less than 16bit are bundled with a .ntfp palette file.
Probably texture palette, with 16bit color numbers. The files can be quite small (eg. only 6 or 8 bytes).
______________________________ .wmif and .wmpf _______________________________
DSi Sudoku rom:\Textures\ has “Wild Magic” .wmif and .wmpf (image+palette) files.
000h 1Bh ID "Wild Magic Image File 3.00",00h
01Bh 4 Palette Filename Length (eg. 0Dh)
01Fh LEN Palette Filename (eg. "BG_Board.wmpf")
... 4 Texture Format (6=4bpp, 7=8bpp)
... 4 Texture Width in pixels
... 4 Texture Height in pixels
... .. Texture data
000h 1Dh ID "Wild Magic Palette File 1.00",00h
01Dh 4 Zero?
021h 4 Number of Colors
025h .. Colors, 16bit (0000h..7FFFh)
Some Nintendo DS games are use the following set of Nitro Studio files for 3D Models:
.NSBMD (ID="BMD0") - Nitro Polygon Model
.NSBTX (ID="BTX0") - Nitro Texture and Palette
.NSBCA (ID="BCA0") - Nitro Skeletal Character Animation
.NSBTP (ID="BTP0") - Nitro Texture Pattern-swap Animation
.NSBTA (ID="BTA0") - Nitro Texture UV-change Animation (aka texcoords?)
.NSBMA (ID="BMA0") - Nitro Material-swap Animation (whut?)
.NSBVA (ID="BVA0") - Nitro Vis... Animation?
Unknown which games are actually using that format… probably some mid-NDS-era titles from Nintendo (early NDS launch titles didn’t use it, 3rd party NDS titles tend to use custom formats, and later DSi mini-games don’t support 3D graphics at all).
Below is an attempt to clean up rather confusing specs (hopefully appearing less confusing, but probably containing some misinterpretations in cases where the original specs where too confusing; the much-too-much confusing parts are left intact and marked as unknown what they might be meant to mean).
000h 1 Dummy 0
001h 1 Amount of "objects"
002h 2 Size of this Header (that is... what? header up to names?)
... .. Probably followed by the three sections mentioned below...?
Unknown Section ;maybe Name lookup (Patricia Tree or Hash or so)?
Data Info Section
Name Section
000h N*16 Name String (in ASCII maybe?) (for each "object")
Aka NSBMD
000h 4 ID "BMD0" (Basic Model Data)
004h 2 Byte Order (FEFFh)
006h 2 Version? (reportedly 1 or 2, in whatever audio/video files?)
008h 4 Total Filesize
00Ch 2 Size of this structure (always 16 ???)
00Eh 2 Number of chunks (1=MDL0 or 2=MDL0+TEX0)
010h 4 Offset to MDL0 Chunk
014h 4 Offset to TEX0 Chunk (if any?) (otherwise TEX0 is in NSBTX file)
000h 4 Chunk ID "MDL0" (Model Block)
004h 4 Chunk Size
008h .. Model Dict (with 32bit offsets to Models from "Block_MDL0")
... .. Models
000h 4 Size of Model (including these 4 bytes)
004h 4 Offset of Additional Model Data ;whut?
008h 4 Offset of Texture & Palette Offset ;?
00Ch 4 Offset of Display List Start ;?
010h 4 Offset of Display List End ;?
014h 1 Unknown
015h 1 Unknown
016h 1 Unknown
017h 1 Amount of Objects ;\
018h 1 Amount of Materials ; what for?
019h 1 Amount of Polygons ; is that just some usage comment,
020h 4 Unknown ; for statistical purposes?
024h 2 Amount of Vertices ;
026h 2 Amount of Surfaces ;
028h 2 Amount of Triangles ;
02Ah 2 Amount of Quads ;/
02Ch 2 Bounding box X (signed fixed point 1.3.12)
02Eh 2 Bounding box Y (signed fixed point 1.3.12)
030h 2 Bounding box Z (signed fixed point 1.3.12)
032h 2 Bounding box Width (signed fixed point 1.3.12)
034h 2 Bounding box Height (signed fixed point 1.3.12)
036h 2 Bounding box Depth (signed fixed point 1.3.12)
038h 4 Runtime use data
03Ch 4 Runtime use data
040h .. Polygonal Object Dict (with 32bit offsets to Objects from
"this Object Header")
... .. Object Definitions
Unknown how above “1.3.12” can be “s32” (maybe actually 17.3.12 ?)
The Transform Flag bits are:
0 Translation (0=Yes, 1=No)
1 Rotation (0=Yes, 1=No)
2 Scaling (0=Yes, 1=No)
3 Rotation Type (0=Rotate A,B,C,D, 1=Pivot A,B)
4-7 Pivot Matrix (0..8, see below) (used when bit1=0 and bit3=1)
Pivot matrix type (0..8):
0: | 1 0 0| 1: | 0 1 0| 2: | 0 0 1|
| 0 A B| | A 0 B| | A B 0|
| 0 B -A| | B 0 -A| | B -A 0|
3: | 0 A B| 4: | A 0 B| 5: | A B 0|
| 1 0 0| | 0 1 0| | 0 0 1|
| 0 B -A| | B 0 -A| | B -A 0|
6: | 0 A B| 7: | A 0 B| 8: | A B 0|
| 0 B -A| | B 0 -A| | B -A 0|
| 1 0 0| | 0 1 0| | 0 0 1|
Non-Pivot does probably also use some kind of matrix made of A,B,C,D…?
000h .. Definitions
Each definition consists of 1 Command Byte + Parameters. Each Parameter is 1 byte.
Cmd Params Description
06h 3 params: Object ID, Parent ID, dummy 0
26h 4 params: Object ID, Parent ID, dummy 0, Stack ID
46h 4 params: Object ID, Parent ID, dummy 0, Stack ID
66h 5 params: Object ID, Parent ID, dummy 0, Stack ID, Restore ID
00h 0 NOP (empty command)
01h 0 End of Bone/Skeleton Section
02h 2 params: Node ID, Visibility
03h 1 Set Polygon Stack ID?
04h 3 params: Material ID, 05h, Polygon ID
05h 1 ??
06h 3 params: Object ID, Parent ID, Dummy 0
07h 1 ??
08h 1 ??
09h 8 ??
0Bh 0 BEGIN (indicate begin of Polygon/Material pairing)
2Bh 0 END (indicate end of Polygon/Material pairing)
These are Material/Polygon pairing commands, 4 bytes.
The lower nibble of 2nd Parameter must be 5.
04h 3 Material ID, 05, Polygon ID
24h 3 ...
44h 3 ...
000h 2 Offset of Texture Section (relative to Texture & Palette Offset)
002h 2 Offset of Palette Section (relative to Texture & Palette Offset)
Dict struct Header;// Material Header: one Header object for each material
{// The 'Data' for this Header is like so:
000h 4 Offset of Material Definition, relative to the start of
this Material Section
}
Texture Section
Dict struct Header;// Texture Header: one Header object for each texture
{// The u32 'Data' for this Header is like so:
000h 2 Offset of Matching Data (relative to Texture & Palette
Offset)
002h 2 Amount of associated Materials (a texture can be in more
than one material)
}
Palette Section
Dict struct Header;// Palette Header: one Header object for each palette
{// The u32 'Data' for this Header is like so:
000h 2 Offset of Matching Data (relative to Texture & Palette
Offset)
002h 2 Amount of associated Materials (a palette can be in more
than one material)
}
Material Definition (repeats * Amount of Materials)
;// Usually 48 bytes for each material
Dict struct Header;// Polygon Header: one Header object for each material
{// The 'Data' for this Header is like so:
000h 4 Offset of Polygon Definition, relative to the start of this
Polygon Section
}
Polygon Definition (repeats * Amount of Polygons) (10h-bytes each)
000h 4 Unknown
004h 4 Unknown
008h 4 Offset of Display List, relative to Polygon Definition
00Ch 4 Size of Display List
Display List
// The Display List is actually packed geometry command.
// See the DStek specification for more information:
// <a href="http://www.akkit.org/info/gbatek.htm#ds3dvideo"><font color="#808080">http://www.akkit.org/info/gbatek.htm#ds3dvideo</font></a>
;uh?
Reportedly same as in BTX0 files, see there for details.
The NSBTX file format stores texture image and palette information.
The “TEX0” Block can be found in certain NSBMD files, not just in NSBTX files.
000h 4 ID "BTX0" (Basic Texture)
004h 2 Byte Order (FEFFh)
006h 2 Version? (reportedly 1 or 2, in whatever audio/video files?)
008h 4 Total Filesize
00Ch 2 Size of this structure (always 16 ???)
00Eh 2 Number of chunks (1=TEX0)
010h 4 Offset to TEX0 Chunk
000h 4 Chunk ID "TEX0" (Texture Block)
004h 4 Chunk Size
008h 4 Padding (0)
00Ch 2 Texture Data Size (bitshift << 3) ;\
00Eh 2 Texture Dict Offset (03Ch) ; Texture
010h 4 Padding (0) ;
014h 4 Texture Data Offset ;/
018h 4 Padding (0)
01Ch 2 Compressed Texture Data Size (bitshift << 3) ;\<-- Size
01Eh 2 Compressed Texture Dict? Offset (03Ch, again?); <-- Dict?
020h 4 Padding (0) ;
024h 4 Compressed Texture Data Offset ; <-- Data
028h 4 Compressed Texture Info Data Offset ;/<-- InfoData?
02Ch 4 Padding (0)
030h 4 Palette Data Size (bitshift << 3) ;\
034h 4 Palette Dict Offset ; Palette
038h 4 Palette Data Offset ;/
03Ch .. Texture Dict (with 8-byte entries, see below)
N/A? ? Compressed Texture Dict? (maybe here? with whatever entries?)
... .. Palette Dict (with 4-byte entries, see below)
... .. Texture Data Section (unknown... maybe bitmap/pixels?)
... .. Compressed Texture Data Section (unknown...)
... .. Compressed Texture Info Data Section (unknown...)
... .. Palette Data Section (unknown... maybe palette/colors?)
000h 2 Texture Offset (bitshift << 3), relative to the start
of Texture Data
002h 2 Parameters ;<-- probably "upper 16bit of TEXIMAGE_PARAM" ?
The format is, using knock-out description:
bit: 15..............0
0 b --CFFFHHHWWW----- <-- uh, is that "0 b" and 17bits???
where:
C = Palette ID
F = Format (0-7)
H = Height (8 << Height)
W = Width (8 << Width)
To Calculate the Data Size of a Texture:
Bit Depth = Format: <0, 8, 2, 4, 8, 2, 8, 16>
Width * Height * BitDepth / 8
004h 1 Width (should match W << 3)
005h 1 Unknown (is 00h or 80h)
006h 1 Height? (can be 0, 1, 2, 4, 8)
007h 1 Unknown (is 80h)
Compressed Texture might have a Dict, too? with whatever entries?
000h 2 u16 ;// (bitshift << 3) Palette Offset, relative to the start
of Palette Data
000h? 2 u16 ;// Unknown (is 0 or 1)
Texture Data Section:
Compressed Texture Data Section:
Compressed Texture Info Data Section:
Palette Data Section:
Unknown
The NSBCA file format stores character skeletal/bone/joint animation data.
000h 4 ID "BCA0" (Basic Character Animation)
004h 2 Byte Order (FEFFh)
006h 2 Version? (reportedly 1 or 2, in whatever audio/video files?)
008h 4 Total Filesize
00Ch 2 Size of this structure (always 16 ???)
00Eh 2 Number of chunks (1=JNT0)
010h 4 Offset to JNT0 Chunk
000h 4 Chunk ID "JNT0" (Joint Block)
004h 4 Chunk Size
008h .. Joint Dict (with 32bit offsets to Joints, from "Block_JNT0")
Reportedly Joints “repeats * Amount of Objects”
Uh, with Amount of Objects stored inside of below?
Maybe rather meant to mean that Joints exist for each Model (not Object)?
000h 4 ID 'J.AC' (Joint Animation Content ?)
004h 2 Amount of Frames
006h 2 Amount of Objects (should be same as in BMD0 file)
008h 4 Unknown
00Ch 4 Offset to Unknown1 chunk. Relative to start of this block.
010h 4 Offset to Unknown2 chunk. Relative to start of this block.
014h 4 Object Info Offset (repeats * Amount of Objects), relative
to start of this Joint Section.
uh, is above meant to be Offset to first Object?
or an array with Offsets to all Objects?
... .. Object Info, for each object (supposedly here?)
... .. Unknown1
... .. Unknown2
... .. Reportedly "end of file" (is that possible with multiple joints?)
000h 2 Flag - Indicates what sort of Transformations are applied.
Bit0 - Unused?
Bit1 T Translate (0=Yes; data follows, 1=No?)
Bit2 - Unused?
Bit3 X Affects how WHICH data is stored but dunno WHERE?
Bit4 Y Affects how WHICH data is stored but dunno WHERE?
Bit5 Z Affects how WHICH data is stored but dunno WHERE?
Bit6 R Rotate (0=Yes; data follows, 1=No?)
Bit7 - Unused?
Bit8 r Affects how WHICH data is stored but dunno WHERE?
Bit9 S Scale (0=Yes; data follows, 1=No?)
Bit10 - Unused?
Bit11 x Affects how WHICH data is stored but dunno WHERE?
Bit12 y Affects how WHICH data is stored but dunno WHERE?
Bit13 z Affects how WHICH data is stored but dunno WHERE?
Bit14 - Unused?
Bit15 - Unused?
002h 1 Unknown (reportedly u32, 1-byte wide ???)
003h 1 ID Number (reportedly u32, 1-byte wide ???)
*** (Unknown what below crap means, it does probably refer to multiple
*** entries, for Translate, Rotate, and Scale; and possibly even to
*** multiple sub-entries for X,Y,Z or whatever?)
Transformation Info (Translation XYZ, Rotation, Scale XYZ) when WHAT=1
000h 4 Actual value (uh, WHICH value?)
Transformation Info (Translation XYZ, Rotation, Scale XYZ) when WHAT=0
000h 2 Unknown - typically 0000h
002h 2 Unknown
004h 4 Offset to data. Relative to "(Object Info Offset + 4)".
000h 2 Reportedly 36, 32 & 0.
002h 2 Unknown (reportedly signed)
004h 2 Unknown (reportedly signed)
"All transformation offsets point somewhere in this section.
It's clearly broken up into parts (Translation, Rotation & Scale),
however I'm not totally sure how data is store in here yet."
____________ DS Files - 3D Video BTP0 (Texture Pattern Animation) ____________
000h 4 ID "BTP0" (Texture Pattern Animation)
... .. XXX
000h 4 Chunk ID "PAT0" (Pattern Block)
004h 4 Chunk Size
... .. Unknown
________________ DS Files - 3D Video BTA0 (Texture Animation) ________________
000h 4 ID "BTA0" (Texture Animation)
... .. XXX
000h 4 Chunk ID "SRT0" (maybe short for Scale/Rotate/Translate?)
004h 4 Chunk Size
... .. Unknown
_______________ DS Files - 3D Video BMA0 (Material Animation) ________________
000h 4 ID "BMA0" (Material Animation)
... .. XXX
000h 4 Chunk ID "MAT0" (Material Block)
004h 4 Chunk Size
... .. Unknown
____________________ DS Files - 3D Video BVA0 (Unknown?) _____________________
000h 4 ID "BVA0" (whatever Vis... Animation?)
... .. XXX
000h 4 Chunk ID "VIS0" (Visibility...?)
004h 4 Chunk Size
... .. Unknown
3D is more or less (about 92%) understood and described.
The NDS 3D hardware consists of a Geometry Engine, and a Rendering Engine.
Geometry commands can be sent via Ports 4000440h and up (or alternately, written directly to Port 4000400h).
The commands include matrix and vector multiplications, the purpose is to rotate/scale/translate coordinates (vertices), the resulting coordinates are stored in Vertex RAM.
Moreover, it allows to assign attributes to the polygons and vertices, that includes vertex colors (or automatically calculated light colors), texture attributes, number of vertices per polygon (three or four), and a number of flags, these attributes are stored in Polygon RAM. Polygon RAM also contains pointers to the corresponding vertices in Vertex RAM.
The hardware includes two sets of Vertex/Polygon RAM, one used by the Geometry Engine, one by the Rendering Engine. The SwapBuffers command simply exchanges these buffers (so the new Geometry Data is passed to the Rendering Engine) (and the old buffer is emptied, so the Geometry engine can write new data to it). Additionally, the two parameter bits from the <previous> SwapBuffers command are copied to the Geometry Engine.
Data that is NOT swapped: SwapBuffers obviously can’t swap Texture memory (so software must take care that Texture memory is kept mapped throughout rendering). Moreover, the rendering control registers (ports 4000060h, and 4000330h..40003BFh) are not swapped (so that values must be kept intact during rendering, too).
The Rendering Engine draws the various Polygons, and outputs them as BG0 layer to the 2D Video controller (which may then output them to the screen, or to the video capture unit). The Rendering part is done automatically by hardware, so the software has little influence on it.
Rendering is done scanline-by-scanline, so there’s only a limited number of clock cycles per scanline, which is limiting the maximum number of polygons per scanline. However, due to the 48-line cache (see below), some scanlines are allowed to exceed that maximum.
Rendering starts 48 lines in advance (while still in the Vblank period) (and does then continue throughout the whole display period), the rendered data is written to a small cache that can hold up to 48 scanlines.
Note: There’s only the 48-line cache (not a full 192-line framebuffer to store the whole rendered image). That is perfectly reasonable since animated data is normally drawn only once (so there would be no need to store it). That, assuming that the Geometry Engine presents new data every frame (otherwise, if the Geometry software is too slow, or if the image isn’t animated, then the hardware is automatically rendering the same image again, and again).
Address Siz Name Expl.
Rendering Engine (per Frame settings)
4000060h 2 DISP3DCNT 3D Display Control Register (R/W)
4000320h 1 RDLINES_COUNT Rendered Line Count Register (R)
4000330h 10h EDGE_COLOR Edge Colors 0..7 (W)
4000340h 1 ALPHA_TEST_REF Alpha-Test Comparision Value (W)
4000350h 4 CLEAR_COLOR Clear Color Attribute Register (W)
4000354h 2 CLEAR_DEPTH Clear Depth Register (W)
4000356h 2 CLRIMAGE_OFFSET Rear-plane Bitmap Scroll Offsets (W)
4000358h 4 FOG_COLOR Fog Color (W)
400035Ch 2 FOG_OFFSET Fog Depth Offset (W)
4000360h 20h FOG_TABLE Fog Density Table, 32 entries (W)
4000380h 40h TOON_TABLE Toon Table, 32 colors (W)
Geometry Engine (per Polygon/Vertex settings)
4000400h 40h GXFIFO Geometry Command FIFO (W)
4000440h ... ... Geometry Command Ports (see below)
4000600h 4 GXSTAT Geometry Engine Status Register (R and R/W)
4000604h 4 RAM_COUNT Polygon List & Vertex RAM Count Register (R)
4000610h 2 DISP_1DOT_DEPTH 1-Dot Polygon Display Boundary Depth (W)
4000620h 10h POS_RESULT Position Test Results (R)
4000630h 6 VEC_RESULT Vector Test Results (R)
4000640h 40h CLIPMTX_RESULT Read Current Clip Coordinates Matrix (R)
4000680h 24h VECMTX_RESULT Read Current Directional Vector Matrix (R)
Table shows Port Address, Command ID, Number of Parameters, and Clock Cycles.
Address Cmd Pa.Cy.
N/A 00h - - NOP - No Operation (for padding packed GXFIFO commands)
4000440h 10h 1 1 MTX_MODE - Set Matrix Mode (W)
4000444h 11h - 17 MTX_PUSH - Push Current Matrix on Stack (W)
4000448h 12h 1 36 MTX_POP - Pop Current Matrix from Stack (W)
400044Ch 13h 1 17 MTX_STORE - Store Current Matrix on Stack (W)
4000450h 14h 1 36 MTX_RESTORE - Restore Current Matrix from Stack (W)
4000454h 15h - 19 MTX_IDENTITY - Load Unit Matrix to Current Matrix (W)
4000458h 16h 16 34 MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
400045Ch 17h 12 30 MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
4000460h 18h 16 35* MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
4000464h 19h 12 31* MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
4000468h 1Ah 9 28* MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
400046Ch 1Bh 3 22 MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
4000470h 1Ch 3 22* MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
4000480h 20h 1 1 COLOR - Directly Set Vertex Color (W)
4000484h 21h 1 9* NORMAL - Set Normal Vector (W)
4000488h 22h 1 1 TEXCOORD - Set Texture Coordinates (W)
400048Ch 23h 2 9 VTX_16 - Set Vertex XYZ Coordinates (W)
4000490h 24h 1 8 VTX_10 - Set Vertex XYZ Coordinates (W)
4000494h 25h 1 8 VTX_XY - Set Vertex XY Coordinates (W)
4000498h 26h 1 8 VTX_XZ - Set Vertex XZ Coordinates (W)
400049Ch 27h 1 8 VTX_YZ - Set Vertex YZ Coordinates (W)
40004A0h 28h 1 8 VTX_DIFF - Set Relative Vertex Coordinates (W)
40004A4h 29h 1 1 POLYGON_ATTR - Set Polygon Attributes (W)
40004A8h 2Ah 1 1 TEXIMAGE_PARAM - Set Texture Parameters (W)
40004ACh 2Bh 1 1 PLTT_BASE - Set Texture Palette Base Address (W)
40004C0h 30h 1 4 DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
40004C4h 31h 1 4 SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
40004C8h 32h 1 6 LIGHT_VECTOR - Set Light's Directional Vector (W)
40004CCh 33h 1 1 LIGHT_COLOR - Set Light Color (W)
40004D0h 34h 32 32 SHININESS - Specular Reflection Shininess Table (W)
4000500h 40h 1 1 BEGIN_VTXS - Start of Vertex List (W)
4000504h 41h - 1 END_VTXS - End of Vertex List (W)
4000540h 50h 1 392 SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
4000580h 60h 1 1 VIEWPORT - Set Viewport (W)
40005C0h 70h 3 103 BOX_TEST - Test if Cuboid Sits inside View Volume (W)
40005C4h 71h 2 9 POS_TEST - Set Position Coordinates for Test (W)
40005C8h 72h 1 5 VEC_TEST - Set Directional Vector for Test (W)
All cycle timings are counted in 33.51MHz units. NORMAL commands takes 9..12 cycles, depending on the number of enabled lights in PolyAttr (Huh, 9..12 (four timings) cycles for 0..4 (five settings) lights?) Total execution time of SwapBuffers is Duration until VBlank, plus 392 cycles.
In MTX_MODE=2 (Simultanous Set), MTX_MULT/TRANS take additional 30 cycles.
0 Texture Mapping (0=Disable, 1=Enable)
1 PolygonAttr Shading (0=Toon Shading, 1=Highlight Shading)
2 Alpha-Test (0=Disable, 1=Enable) (see ALPHA_TEST_REF)
3 Alpha-Blending (0=Disable, 1=Enable) (see various Alpha values)
4 Anti-Aliasing (0=Disable, 1=Enable)
5 Edge-Marking (0=Disable, 1=Enable) (see EDGE_COLOR)
6 Fog Color/Alpha Mode (0=Alpha and Color, 1=Only Alpha) (see FOG_COLOR)
7 Fog Master Enable (0=Disable, 1=Enable)
8-11 Fog Depth Shift (FOG_STEP=400h shr FOG_SHIFT) (see FOG_OFFSET)
12 Color Buffer RDLINES Underflow (0=None, 1=Underflow/Acknowledge)
13 Polygon/Vertex RAM Overflow (0=None, 1=Overflow/Acknowledge)
14 Rear-Plane Mode (0=Blank, 1=Bitmap)
15-31 Not used
SwapBuffers exchanges the two sets of Polygon/Vertex RAM buffers, that is, the newly defined polygons/vertices are passed to the rendering engine (and will be displayed in following frame(s)). The other buffer is emptied, and passed to the Geometry Engine (to be filled with new polygons/vertices by Geometry Commands).
0 Translucent polygon Y-sorting (0=Auto-sort, 1=Manual-sort)
1 Depth Buffering (0=With Z-value, 1=With W-value)
(mode 1 does not function properly with orthogonal projections)
2-31 Not used
SwapBuffers isn’t executed until next VBlank (Scanline 192) (the Geometry Engine is halted for that duration). SwapBuffers should not be issued within Begin/End. The two parameter bits of the SwapBuffers command are used for the following gxcommands (ie. not for the old gxcommands prior to SwapBuffers).
SwapBuffers does lock-up the 3D hardware if an incomplete polygon list has been defined (eg. a triangle with only 2 vertices). On lock-up, only 2D video is kept working, any wait-loops for GXSTAT.27 will hang the program. Once lock-up has occured, there seems to be no way to recover by software, not by sending the missing veric(es), and not even by pulsing POWCNT1.Bit2-3.
0-7 Screen/BG0 Coordinate X1 (0..255) (For Fullscreen: 0=Left-most)
8-15 Screen/BG0 Coordinate Y1 (0..191) (For Fullscreen: 0=Bottom-most)
16-23 Screen/BG0 Coordinate X2 (0..255) (For Fullscreen: 255=Right-most)
24-31 Screen/BG0 Coordinate Y2 (0..191) (For Fullscreen: 191=Top-most)
Coordinate 0,0 is the lower-left (unlike for 2D where it’d be upper-left).
The 3D view-volume (size as defined by the Projection Matrix) is automatically scaled to match into the Viewport area. Although polygon vertices are clipped to the view-volume, some vertices may still exceed to X2,Y1 (lower-right) boundary by one pixel, due to some sort of rounding errors. The Viewport settings don’t affect the size or position of the 3D Rear-Plane. Viewport should not be issued within Begin/End.
1-Dot Polygons are very small, or very distant polygons, which would be rendered as a single pixel on screen. Polygons with a depth value greater (more distant) than DISP_1DOT_DEPTH can be automatically hidden; in order to reduce memory consumption, or to reduce dirt on the screen.
0-14 W-Coordinate (Unsigned, 12bit integer, 3bit fractional part)
15-31 Not used (0000h=Closest, 7FFFh=Most Distant)
The DISP_1DOT_DEPTH comparision can be enabled/disabled per polygon (via POLYGON_ATTR.Bit13), so “important” polygons can be displayed regardless of their size and distance.
Note: The comparision is always using the W-coordinate of the vertex (not the Z-coordinate) (ie. no matter if using Z-buffering, or W-buffering). The polygon is rendered if at least one of its vertices is having a w-coordinate less or equal than DISP_1DOT_DEPTH. NB. despite of checking the w-coords of ALL vertices, the polygon is rendered using the color/depth/texture of its FIRST vertex.
Note: The hardware does round-up the width and height of all polygons to at least 1, so polygons of 0x0, 1x0, 0x1, and 1x1 dots will be all rounded-up to a size of 1x1. Of which, the so-called “1dot” depth check is applied only to the 0x0 dot variant (so “0dot” depth check would be a better name for it).
Caution: Although DISP_1DOT_DEPTH is a Geometry Engine parameter, it is NOT routed through GXFIFO, ie. changes will take place immediately, and will affect all following polygons, including such that are still in GXFIFO. Workaround: ensure that GXFIFO is empty before changing this parameter.
Alpha Test can be enabled in DISP3DCNT.Bit2. When enabled, pixels are rendered only if their Alpha value is GREATER than ALPHA_TEST_REF. Otherwise, when disabled, pixels are rendered only if their Alpha value is GREATER than zero. Alpha Test is performed on the final polygon pixels (ie. after texture blending).
0-4 Alpha-Test Comparision Value (0..31) (Draw pixels if Alpha>AlphaRef)
5-31 Not used
Value 00h is effectively the same as when Alpha Test is disabled. Value 1Fh hides all polygons, including opaque ones.
Used to send packed commands, unpacked commands,
0-7 First Packed Command (or Unpacked Command)
8-15 Second Packed Command (or 00h=None)
16-23 Third Packed Command (or 00h=None)
24-31 Fourth Packed Command (or 00h=None)
and parameters,
0-31 Parameter data for the previously sent (packed) command(s)
to the Geometry engine.
The FIFO has 256 entries, additionally, there is a PIPE with four entries (giving a total of 260 entries). If the FIFO is empty, and if the PIPE isn’t full, then data is moved directly into the PIPE, otherwise it is moved into the FIFO. If the PIPE runs half empty (less than 3 entries) then 2 entries are moved from the FIFO to the PIPE. The state of the FIFO can be obtained in GXSTAT.Bit16-26, observe that there may be still data in the PIPE, even if the FIFO is empty. Check the busy flag in GXSTAT.Bit27 to see if the PIPE or FIFO contains data (or if a command is still executing).
Each PIPE/FIFO entry consists of 40bits of data (8bit command code, plus 32bit parameter value). Commands without parameters occupy 1 entry, and Commands with N parameters occupy N entries.
Geometry commands can be indirectly sent to the FIFO via ports 4000440h and up.
For a command with N paramters: issue N writes to the port.
For a command without parameters: issue one dummy-write to the port.
That mechanism puts the 8bit command + 32bit parameter into the FIFO/PIPE.
If the FIFO is full, then a wait is generated until data is removed from the FIFO, ie. the STR opcode gets freezed, during the wait, the bus cannot be used even by DMA, interrupts, or by the NDS7 CPU.
Larger pre-calculated data blocks can be sent directly to the FIFO. This is usually done via DMA (use DMA in Geometry Command Mode, 32bit units, Dest=4000400h/fixed, Length=NumWords, Repeat=0). The timings are handled automatically, ie. the system (should) doesn’t freeze when the FIFO is full (see below Overkill note though). DMA starts when the FIFO becomes less than half full, the DMA does then write 112 words to the GXFIFO register (or less, if the remaining DMA transfer length gets zero).
If desired, STR,STRD,STM opcodes can be used to write to the FIFO.
Opcodes that write more than one 32bit value (ie. STRD and STM) can be used to send ONE UNPACKED command, plus any parameters which belong to that command. After that, there must be a 1 cycle delay before sending the next command (ie. one cannot sent more than one command at once with a single opcode, each command must be invoked by a new opcode). STRD and STM can be used because the GXFIFO register is mirrored to 4000400h..43Fh (16 words).
As with Ports 4000440h and up, the CPU gets stopped if (and as long as) the FIFO is full.
- command1 (upper 24bit zero)
- parameter(s) for command1 (if any)
- command2 (upper 24bit zero)
- parameter(s) for command2 (if any)
- command3 (upper 24bit zero)
- parameter(s) for command3 (if any)
- command1,2,3,4 packed into one 32bit value (all bits used)
- parameter(s) for command1 (if any)
- parameter(s) for command2 (if any)
- parameter(s) for command3 (if any)
- parameter(s) for command4 (top-most packed command MUST have parameters)
- command5,6 packed into one 32bit value (upper 16bit zero)
- parameter(s) for command5 (if any)
- parameter(s) for command6 (top-most packed command MUST have parameters)
- command7,8,9 packed into one 32bit value (upper 8bit zero)
- parameter(s) for command7 (if any)
- parameter(s) for command8 (if any)
- parameter(s) for command9 (top-most packed command MUST have parameters)
Packed commands are first decompressed and then stored in command the FIFO.
Normally, the 112 word limit ensures that the FIFO (256 entries) doesn’t get full, however, this limit is much too high for sending a lot of “Packed Commands Without Parameters” (ie. PUSH, IDENTITY, or END) - eg. sending 112 x Packed(00151515h) to GXFIFO would write 336 x Cmd(15h) to the FIFO, which is causing the FIFO to get full, and which is causing the DMA (and CPU) to be paused (for several seconds, in WORST case) until enough FIFO commands have been processed to allow the DMA to finish the 112 word transfer.
Not sure if there’s much chance to get Overkills in practice. Normally most commands DO have parameters, and so, usually even LESS than 112 FIFO entries are occupied (since 8bit commands with 32bit parameters are merged into single 40bit FIFO entries).
Invalid commands (anything else than 10h..1Ch, 20h..2Bh, 30h..33h, 40h..41h, 50h, 60h, or 70h..72h) seem to be simply ignored by the hardware (at least, testing has confirmed that they do not fetch any parameters from the gxfifo).
0-1 Matrix Mode (0..3)
0 Projection Matrix
1 Position Matrix (aka Modelview Matrix)
2 Position & Vector Simultaneous Set mode (used for Light+VEC_TEST)
3 Texture Matrix (see DS 3D Texture Coordinates chapter)
2-31 Not used
Selects the current Matrix, all following MTX commands (load, multiply, push, pop, etc.) are applied to that matrix. In Mode 2, all MTX commands are applied to both the Position and Vector matrices. There are two special cases:
MTX_SCALE in Mode 2: uses ONLY Position Matrix
MTX_PUSH/POP/STORE/RESTORE in Mode 1: uses BOTH Position AND Vector Matrices
Ie. the four stack commands act like mode 2 (even when in mode 1; keeping the two stacks somewhat in sync), and scale acts like mode 1 (even when in mode 2; keeping the light vector length’s intact).
For the above cases, the commands do always act like mode 1, even when they are i
Sets C=I. Parameters: None
The Identity Matrix (I), aka Unit Matrix, consists of all zeroes, with a diagonal row of ones. A matrix multiplied by the Unit Matrix is left unchanged.
Sets C=M. Parameters: 16, m[0..15]
Sets C=M. Parameters: 12, m[0..11]
Sets C=M*C. Parameters: 16, m[0..15]
Sets C=M*C. Parameters: 12, m[0..11]
Sets C=M*C. Parameters: 9, m[0..8]
Sets C=M*C. Parameters: 3, m[0..2]
Note: MTX_SCALE doesn’t change Vector Matrix (even when in MTX_MODE=2) (that’s done so for keeping the length of the light vector’s intact).
Sets C=M*C. Parameters: 3, m[0..2] (x,y,z position)
This 64-byte region (16 words) contains the m[0..15] values of the Current Clip Coordinates Matrix, arranged in 4x4 Matrix format. Make sure that the Geometry Engine is stopped (GXSTAT.27) before reading from these registers.
The Clip Matrix is internally used to convert vertices to screen coordinates, and is internally re-calculated anytime when changing the Position or Projection matrices:
ClipMatrix = PositionMatrix * ProjectionMatrix
To read only the Position Matrix, or only the Projection Matrix: Use Load Identity on the OTHER matrix, so the ClipMatrix becomes equal to the DESIRED matrix (multiplied by the Identity Matrix, which has no effect on the result).
This 36-byte region (9 words) contains the m[0..8] values of the Current Directional Vector Matrix, arranged in 3x3 Matrix format (the fourth row/column may contain any values).
Make sure that the Geometry Engine is stopped (GXSTAT.27) before reading from these registers.
Essentially, all matrices in the NDS are 4x4 Matrices, consisting of 16 values, m[0..15]. Each element is a signed fixed-point 32bit number, with a fractional part in the lower 12bits.
The other Matrix Types are used to reduce the number of parameters being transferred, for example, 3x3 Matrix requires only nine parameters, the other seven elements are automatically set to 0 or 1.0 (whereas “1.0” means “1 SHL 12” in 12bit fixed-point notation).
_ 4x4 Matrix _ _ Identity Matrix _
| m[0] m[1] m[2] m[3] | | 1.0 0 0 0 |
| m[4] m[5] m[6] m[7] | | 0 1.0 0 0 |
| m[8] m[9] m[10] m[11] | | 0 0 1.0 0 |
|_m[12] m[13] m[14] m[15]_| |_ 0 0 0 1.0 _|
_ 4x3 Matrix _ _ Translation Matrix _
| m[0] m[1] m[2] 0 | | 1.0 0 0 0 |
| m[3] m[4] m[5] 0 | | 0 1.0 0 0 |
| m[6] m[7] m[8] 0 | | 0 0 1.0 0 |
|_m[9] m[10] m[11] 1.0 _| |_m[0] m[1] m[2] 1.0 _|
_ 3x3 Matrix _ _ Scale Matrix _
| m[0] m[1] m[2] 0 | | m[0] 0 0 0 |
| m[3] m[4] m[5] 0 | | 0 m[1] 0 0 |
| m[6] m[7] m[8] 0 | | 0 0 m[2] 0 |
|_ 0 0 0 1.0 _| |_ 0 0 0 1.0 _|
The NDS has three Matrix Stacks, and two Matrix Stack Pointers (the Coordinate Matrix stack pointer is also shared for Directional Matrix Stack).
Matrix Stack________Valid Stack Area____Stack Pointer___________________
Projection Stack 0..0 (1 entry) 0..1 (1bit) (GXSTAT: 1bit)
Coordinate Stack 0..30 (31 entries) 0..63 (6bit) (GXSTAT: 5bit only)
Directional Stack 0..30 (31 entries) (uses Coordinate Stack Pointer)
Texture Stack One..None? 0..1 (1bit) (GXSTAT: N/A)
Which of the stacks/matrices depends on the current Matrix Mode (as usually,
but with one exception; stack operations MTX_PUSH/POP/STORE/RESTORE in Mode 1 are acting same as in Mode 2):
MTX_MODE = 0 --> Projection Stack
MTX_MODE = 1 or 2 --> BOTH Coordinate AND Directional Stack
MTX_MODE = 3 --> Texture Stack
The initial value of the Stack Pointers is zero, the current value of the pointers can be read from GXSTAT (read-only), that register does also indicate stack overflows (errors flag gets set on read/write to invalid entries, ie. entries 1 or 1Fh..3Fh). For all stacks, the upper half (ie. 1 or 20h..3Fh) are mirrors of the lower half (ie. 0 or 0..1Fh).
Parameters: None. Sets [S]=C, and then S=S+1.
Sets S=S-N, and then C=[S].
Parameter Bit0-5: Stack Offset (signed value, -30..+31) (usually +1)
Parameter Bit6-31: Not used
Offset N=(+1) pops the most recently pushed value, larger offsets of N>1 will “deallocate” N values (and load the Nth value into C). Zero or negative values can be used to pop previously “deallocated” values.
The stack has only one level (at address 0) in projection mode, in that mode, the parameter value is ignored, the offset is always +1 in that mode.
Sets [N]=C. The stack pointer S is not used, and is left unchanged.
Parameter Bit0-4: Stack Address (0..30) (31 causes overflow in GXSTAT.15)
Parameter Bit5-31: Not used
The stack has only one level (at address 0) in projection mode, in that mode, the parameter value is ignored.
Sets C=[N]. The stack pointer S is not used, and is left unchanged.
Parameter Bit0-4: Stack Address (0..30) (31 causes overflow in GXSTAT.15)
Parameter Bit5-31: Not used
The stack has only one level (at address 0) in projection mode, in that mode, the parameter value is ignored.
In Projection mode, the parameter for POP, STORE, and RESTORE is unused - not sure if the parameter (ie. a dummy value) is - or is not - to be written to the command FIFO?
There appear to be actually 32 entries in Coordinate & Directional Stacks, entry 31 appears to exist, and appears to be read/write-able (although the stack overflow flag gets set when accessing it).
The most important matrix is the Projection Matrix (to be initialized with MTX_MODE=0 via MTX_LOAD_4x4 command). It does specify the dimensions of the view volume.
With Perspective Projections more distant objects will appear smaller, with Orthogonal Projects the size of the objects is always same regardless of their distance.
Perspective Projection Orthogonal Projection
__ __________
top __..--'' | top | |
| view | | view |
Eye ----|--------->| Eye ----|--------->|
|__volume | | volume |
bottom ''--..__| bottom|__________|
near far near far
Correctly initializing the projection matrix (as shown in the examples below) can be quite difficult (mind that fixed point multiply/divide requires to adjust the fixed-point width before/after calculation). For beginners, it may be recommended to start with a simple Identity Matrix (MTX_IDENTITY command) used as Projection Matrix (ie. Ortho with t,b,l,r set to +/-1).
| (2.0)/(r-l) 0 0 0 |
| 0 (2.0)/(t-b) 0 0 |
| 0 0 (2.0)/(n-f) 0 |
| (l+r)/(l-r) (b+t)/(b-t) (n+f)/(n-f) 1.0 |
n,f specify the distance from eye to near and far clip planes. t,b,l,r are the coordinates of near clip plane (top,bottom,left,right). For a symmetrical view (ie. the straight-ahead view line centered in the middle of viewport) t,b,l,r should be usually t=+ysiz/2, b=-ysiz/2, r=+xsiz/2, l=-xsiz/2; the (xsiz/ysiz) ratio should be usually equal to the viewport’s (width/heigh) ratio. Examples for a asymmetrical view would be b=0 (frog’s view), or t=0 (bird’s view).
| (2*n)/(r-l) 0 0 0 |
| 0 (2*n)/(t-b) 0 0 |
| (r+l)/(r-l) (t+b)/(t-b) (n+f)/(n-f) -1.0 |
| 0 0 (2*n*f)/(n-f) 0 |
n,f,t,b,l,r have same meanings as above (Ortho), the difference is that more distant objects will appear smaller with Perspective Projection (unlike Orthogonal Projection where the size isn’t affected by the distance).
| cos/(asp*sin) 0 0 0 |
| 0 cos/sin 0 0 |
| 0 0 (n+f)/(n-f) -1.0 |
| 0 0 (2*n*f)/(n-f) 0 |
Quite the same as above (Frustum), but with symmetrical t,b values (which are in this case obtained from a vertical view range specified in degrees), and l,r are matched to the aspect ratio of the viewport (asp=height/width).
After initializing the Projection Matrix, you may multiply it with Rotate and/or Translation Matrices to change camera’s position and view direction.
The MTX_IDENTITY command can be used to initialize the Position Matrix before doing any Translation/Scaling/Rotation, for example:
Load(Identity) ;no rotation/scaling used
Load(Identity), Mul(Rotate), Mul(Scale) ;rotation/scaling (not so efficient)
Load(Rotate), Mul(Scale) ;rotation/scaling (more efficient)
Rotation can be performed with MTX_MULT_3x3 command, simple examples are:
Around X-Axis Around Y-Axis Around Z-Axis
| 1.0 0 0 | | cos 0 sin | | cos sin 0 |
| 0 cos sin | | 0 1.0 0 | | -sin cos 0 |
| 0 -sin cos | | -sin 0 cos | | 0 0 1.0 |
The MTX_SCALE command allows to adjust the size of the polygon. The x,y,z parameters should be normally all having the same value, x=y=z (unless if you want to change only the height of the object, for example). Identical results can be obtained with MTX_MULT commands, however, when using lighting (MTX_MODE=2), then scaling should be done ONLY with MTX_SCALE (which keeps the length of the light’s directional vector intact).
The MTX_TRANS command allows to move polygons to the desired position. The polygon VTX commands are spanning only a small range of coordinates (near zero-coordinate), so translation is required to move the polygons to other locations in the world coordinates. Aside from that, translation is useful for moved objects (at variable coordinates), and for re-using an object at various locations (eg. you can create a forest by translating a tree to different coordinates).
The Matrix must be set up BEFORE sending the Vertices (which are then automatically multiplied by the matrix). When using multiple matrices multiplied with each other: Mind that, for matrix maths A*B is NOT the same as B*A. For example, if you combine Rotate and Translate Matrices, the object will be either rotated around it’s own zero-coordinate, or around world-space zero-coordinate, depending on the multiply order.
Below is a crash-course on matrix maths. Most of it is carried out automatically by the hardware. So this chapter is relevant only if you are interested in details about what happens inside of the 3D engine.
Matrix multiplication, C = A * B, is possible only if the number of columns in A is equal to the number of rows in B, so it works fine with the 4x4 matrices which are used in the NDS. For the multiplication, assume matrix C to consist of elements cyx, and respecitively, matrix A and B to consist of elements ayx and byx. So that C = A * B looks like:
| c11 c12 c13 c14 | | a11 a12 a13 a14 | | b11 b12 b13 b14 |
| c21 c22 c23 c24 | = | a21 a22 a23 a24 | * | b21 b22 b23 b24 |
| c31 c32 c33 c34 | | a31 a32 a33 a34 | | b31 b32 b33 b34 |
| c41 c42 c43 c44 | | a41 a42 a43 a44 | | b41 b42 b43 b44 |
Each element in C is calculated by multiplying the elements from one row in A by the elements from the corresponding column in B, and then taking the sum of the products, ie.
cyx = ay1*b1x + ay2*b2x + ay3*b3x + ay4*b4x
In total, that requires 64 multiplications (four multiplications for each of the 16 cyx elements), and 48 additions (three per cyx element), the hardware carries out that operation at a relative decent speed of 30..35 clock cycles, possibly by performing several multiplications simultaneously with separate multiply units.
Observe that for matrix multiplication, A*B is NOT the same as B*A.
Vectors are Matrices with only one row, or only one column. Multiplication works as for normal matrices; the number of rows/columns must match up, repectively, row-vectors can be multiplied by matrices; and matrices can be multiplied by column-vectors (but not vice-versa). Eg. C = A * B:
| b11 b12 b13 b14 |
| c11 c12 c13 c14 | = | a11 a12 a13 a14 | * | b21 b22 b23 b24 |
| b31 b32 b33 b34 |
| b41 b42 b43 b44 |
The formula for calculating the separate elements is same as above,
cyx = ay1*b1x + ay2*b2x + ay3*b3x + ay4*b4x
Of which, C and A have only one y-index, so one may replace “cyx and ayx” by “c1x and a1x”, or completely leave out the y-index, ie. “cx and ax”.
Simply multiply all elements of the Matrix by the number, C = A * n:
cyx = ayx*n
Of course, works also with vectors (matrices with only one row/column).
Both matrices must have the same number of rows & columns, add/subtract all elements with corresponding elements in other matrix, C = A +/- B:
cyx = ayx +/- byx
Of course, works also with vectors (two matrices with only one row/column).
A vector, for example (x,y,z), consists of offsets along x-,y-, and z-axis. The line from origin to origin-plus-offset is having two characteristics: A direction, and a length.
The length (aka magnitude) can be calculated as L=sqrt(x^2+y^2+z^2).
This can be processed as LineVector*RowVector, so the result is a number (aka scalar) (aka a matrix with only 1x1 elements). Multiplying two (normalized) vectors results in: “cos(angle)=vec1*vec2”, ie. the cosine of the angle between the two vectors (eg. used for light vectors). Multiplying a vector with itself, and taking the square root of the result obtains its length, ie. “length=sqrt(vec^2)”.
That stuff should be done with 3-dimensional vectors (not 4-dimensionals).
Normalized Vectors (aka Unit Vectors) are vectors with length=1.0. To normalize a vector, divide its coordinates by its length, ie. x=x/L, y=y/L, z=z/L, the direction remains the same, but the length is now 1.0.
On the NDS, normalized vectors should have a length of something less than 1.0 (eg. something like 0.99) because several NDS registers are limited to 1bit sign, 0bit integer, Nbit fractional part (so vectors that are parallel to the x,y,z axes, or that become parallel to them after rotation, cannot have a length of 1.0).
The NDS uses fixed-point numbers (rather than floating point numbers). Addition and Subtraction works as with normal integers, provided that the fractional part is the same for both numbers. If it is not the same: Shift-left the value with the smaller fractional part.
For multiplication, the fractional part of result is the sum of the fractional parts (eg. 12bit fraction * 12bit fraction = 24bit fraction; shift-right the result by 12 to convert it 12bit fraction). The NDS matrix multiply unit is maintaining the full 24bit fraction when processing the
cyx = ay1*b1x + ay2*b2x + ay3*b3x + ay4*b4x
formula, ie. the three additions are using full 24bit fractions (with carry-outs to upper bits), the final result of the additions is then shifted-right by 12.
For division, it’s vice versa, the fractions of the operands are substracted, 24bit fraction / 12bit fraction = 12bit fraction. When dividing two 12bit numbers, shift-left the first number by 12 before division to get a result with 12bit fractional part.
The NDS uses four-dimensional matrices and vectors, ie. matrices with 4x4 elements, and vectors with 4 elements. The first three elements are associated with the X,Y,Z-axes of the three-dimensional space. The fourth element is somewhat a “W-axis”.
With 4-dimensional matrices, the Translate matrix can be used to move an object to another position. Ie. once when you’ve setup a matrix (which may consists of pre-multiplied scaling, rotation, translation matrices), then that matrix can be used on vertices to perform the rotation, scaling, translation all-at-once; by a single Vector*Matrix operation.
With 3-dimensional matrices, translation would require a separate addition, additionally to the multiply operation.
0-3 Light 0..3 Enable Flags (each bit: 0=Disable, 1=Enable)
4-5 Polygon Mode (0=Modulation,1=Decal,2=Toon/Highlight Shading,3=Shadow)
6 Polygon Back Surface (0=Hide, 1=Render) ;Line-segments are always
7 Polygon Front Surface (0=Hide, 1=Render) ;rendered (no front/back)
8-10 Not used
11 Depth-value for Translucent Pixels (0=Keep Old, 1=Set New Depth)
12 Far-plane intersecting polygons (0=Hide, 1=Render/clipped)
13 1-Dot polygons behind DISP_1DOT_DEPTH (0=Hide, 1=Render)
14 Depth Test, Draw Pixels with Depth (0=Less, 1=Equal) (usually 0)
15 Fog Enable (0=Disable, 1=Enable)
16-20 Alpha (0=Wire-Frame, 1..30=Translucent, 31=Solid)
21-23 Not used
24-29 Polygon ID (00h..3Fh, used for translucent, shadow, and edge-marking)
30-31 Not used
Writes to POLYGON_ATTR have no effect until next BEGIN_VTXS command.
Changes to the Light bits have no effect until lighting is re-calculated by Normal command. The interior of Wire-frame polygons is transparent (Alpha=0), and only the lines at the polygon edges are rendered, using a fixed Alpha value of 31.
Parameter 1, Bit 0-4 Red
Parameter 1, Bit 5-9 Green
Parameter 1, Bit 10-14 Blue
Parameter 1, Bit 15-31 Not used
The 5bit RGB values are internally expanded to 6bit RGB as follows: X=X*2+(X+31)/32, ie. zero remains zero, all other values are X=X*2+1.
Aside from by using the Color command, the color can be also changed by MaterialColor0 command (if MaterialColor0.Bit15 is set, it acts identical as the Color Command), and by the Normal command (which calculates the color based on light/material parameters).
The Depth Test compares the depth of the pixels of the polygon with the depth of previously rendered polygons (or of the rear plane if there have been none rendered yet). The new pixels are drawn if the new depth is Less (closer to the camera), or if it is Equal, as selected by POLYGON_ATTR.Bit14.
Normally, Depth Equal would work only exact matches (ie. if the overlapping polygons have exactly the same coordinates; and thus have the same rounding errors), however, the NDS hardware is allowing “Equal” to have a tolerance of +/-200h (within the 24bit depth range of 0..FFFFFFh), that may bypass rounding errors, but it may also cause nearby polygons to be accidently treated to have equal depth.
The DS supports polygons with 3 or 4 edges, triangles and quadliterals.
The position of the edges is defined by vertices, each consisting of (x,y,z) values.
For Line Segments, use Triangles with twice the same vertex, Line Segments are rendered always because they do not have any front and back sides.
The Prohibited Quad shapes may produce unintended results, namely, that are Quads with crossed sides, and quads with angles greater than 180 degrees.
Separate Tri. Triangle Strips Line Segment
v0 v2___v4____v6
|\ v3 /|\ |\ /\ v0 v1
| \ /\ v0( | \ | \ / \ ------
|__\ /__\ \|__\|__\/____\ v2
v1 v2 v4 v5 v1 v3 v5 v7
Separate Quads Quadliteral Strips Prohibited Quads
v0__v3 v0__v2____v4 v10__ v0__v3 v4
/ \ v4____v7 / \ |\ _____ / /v11 \/ |\
/ \ | \ / \ | |v6 v8| / /\ v5| \
/______\ |_____\ /______\___|_|_____|/ /__\ /___\
v1 v2 v5 v6 v1 v3 v5 v7 v9 v2 v1 v6 v7
The vertices are normally arranged anti-clockwise, except that: in triangle-strips each second polygon uses clockwise arranged vertices, and quad-strips are sorts of “up-down” arranged (whereas “up” and “down” may be anywhere due to rotation). Other arrangements may result in quads with crossed lines, or may swap the front and back sides of the polygon (above examples are showing the front sides).
Parameter 1, Bit 0-1 Primitive Type (0..3, see below)
Parameter 1, Bit 2-31 Not used
Indicates the Start of a Vertex List, and its Primitive Type:
0 Separate Triangle(s) ;3*N vertices per N triangles
1 Separate Quadliteral(s) ;4*N vertices per N quads
2 Triangle Strips ;3+(N-1) vertices per N triangles
3 Quadliteral Strips ;4+(N-1)*2 vertices per N quads
The BEGIN_VTX command should be followed by VTX_-commands to define the Vertices of the list, and should be then terminated by END_VTX command.
BEGIN_VTX additionally applies changes to POLYGON_ATTR.
Parameters: None. This is a Dummy command for OpenGL compatibility. It should be used to terminate a BEGIN_VTX, VTX_<values> sequence. END_VTXS is possibly required for Nintendo’s software emulator? On real NDS consoles (and in no$gba) it does have no effect, it can be left out, or can be issued multiple times inside of a vertex list, without disturbing the display.
Parameter 1, Bit 0-15 X-Coordinate (signed, with 12bit fractional part)
Parameter 1, Bit 16-31 Y-Coordinate (signed, with 12bit fractional part)
Parameter 2, Bit 0-15 Z-Coordinate (signed, with 12bit fractional part)
Parameter 2, Bit 16-31 Not used
Parameter 1, Bit 0-9 X-Coordinate (signed, with 6bit fractional part)
Parameter 1, Bit 10-19 Y-Coordinate (signed, with 6bit fractional part)
Parameter 1, Bit 20-29 Z-Coordinate (signed, with 6bit fractional part)
Parameter 1, Bit 30-31 Not used
Same as VTX_16, with only one parameter, with smaller fractional part.
Parameter 1, Bit 0-15 X-Coordinate (signed, with 12bit fractional part)
Parameter 1, Bit 16-31 Y-Coordinate (signed, with 12bit fractional part)
The Z-Coordinate is kept unchanged, and re-uses the value from previous VTX.
Parameter 1, Bit 0-15 X-Coordinate (signed, with 12bit fractional part)
Parameter 1, Bit 16-31 Z-Coordinate (signed, with 12bit fractional part)
The Y-Coordinate is kept unchanged, and re-uses the value from previous VTX.
Parameter 1, Bit 0-15 Y-Coordinate (signed, with 12bit fractional part)
Parameter 1, Bit 16-31 Z-Coordinate (signed, with 12bit fractional part)
The X-Coordinate is kept unchanged, and re-uses the value from previous VTX.
Parameter 1, Bit 0-9 X-Difference (signed, with 9/12bit fractional part)
Parameter 1, Bit 10-19 Y-Difference (signed, with 9/12bit fractional part)
Parameter 1, Bit 20-29 Z-Difference (signed, with 9/12bit fractional part)
Parameter 1, Bit 30-31 Not used
Sets XYZ-Coordinate relative to the XYZ-Coordinates from previous VTX. In detail: The 9bit fractional values are divided by 8 (sign expanded to 12bit fractions, in range +/-0.125), and that 12bit fraction is then added to the old vtx coordinates. The result of the addition should not overflow 16bit vertex coordinate range (1bit sign, 3bit integer, 12bit fraction).
On each VTX command, the viewport coordinates of the vertex are calculated and stored in Vertex RAM,
( xx, yy, zz, ww ) = ( x, y, z, 1.0 ) * ClipMatrix
The actual screen position (in pixels) is then,
screen_x = (xx+ww)*viewport_width / (2*ww) + viewport_x1
screen_y = (yy+ww)*viewport_height / (2*ww) + viewport_y1
Each VTX command that completes the definition of a polygon (ie. each 3rd for Separate Trangles) does additionally store data in Polygon List RAM.
VTX commands may be issued only between Begin and End commands.
Polygons are clipped to the 6 sides of the view volume (ie. to the left, right, top, bottom, near, and far edges). If one or more vertic(es) exceed one of these sides, then these vertic(es) are replaced by two newly created vertices (which are located on the intersections of the polygon edges and the view volume edge).
Depending on the number of clipped vertic(es), this may increase or decrease the number of entries in Vertex RAM (ie. minus N clipped vertices, plus 2 new vertices). Also, clipped polygons which are part of polygon strips are converted to separate polygons (which does increase number of entries in Vertex RAM). Polygons that are fully outside of the View Volume aren’t stored in Vertex RAM, nor in Polygon RAM (the only exception are polygons that are located exactly one pixel below of, or right of lower/right edges, which appear to be accidently stored in memory).
The lighting operation is performed by executing the Normal command (which sets the VertexColor based on the Light/Material parameters) (to the rest of the hardware it doesn’t matter if the VertexColor was set by Color command or by Normal command). Light is calculated only for the Front side of the polygon (assuming that the Normal is matched to that side), so the Back side will be (incorrectly) using the same color.
Sets direction of the specified light (ie. the light selected in Bit30-31).
0-9 Directional Vector's X component (1bit sign + 9bit fractional part)
10-19 Directional Vector's Y component (1bit sign + 9bit fractional part)
20-29 Directional Vector's Z component (1bit sign + 9bit fractional part)
30-31 Light Number (0..3)
Upon executing this command, the incoming vector is multiplied by the current Directional Matrix, the result is then applied as LightVector. This allows to rotate the light direction. However, normally, to keep the light unrotated, be sure to use LoadIdentity (in MtxMode=2) before setting the LightVector.
Sets the color of the specified light (ie. the light selected in Bit30-31).
0-4 Red (0..1Fh) ;\light color this will be combined with
5-9 Green (0..1Fh) ; diffuse, specular, and ambient colors
10-14 Blue (0..1Fh) ;/upon execution of the normal command
15-29 Not used
30-31 Light Number (0..3)
0-4 Diffuse Reflection Red ;\light(s) that directly hits the polygon,
5-9 Diffuse Reflection Green ; ie. max when NormalVector has opposite
10-14 Diffuse Reflection Blue ;/direction of LightVector
15 Set Vertex Color (0=No, 1=Set Diffuse Reflection Color as Vertex Color)
16-20 Ambient Reflection Red ;\light(s) that indirectly hits the polygon,
21-25 Ambient Reflection Green ; ie. assuming that light is reflected by
26-30 Ambient Reflection Blue ;/walls/floor, regardless of LightVector
31 Not used
With Bit15 set, the lower 15bits are applied as VertexColor (exactly as when when executing the Color command), the purpose is to use it as default color (eg. when outcommenting the Normal command), normally, when using lighting, the color setting gets overwritten (as soon as executing the Normal command).
0-4 Specular Reflection Red ;\light(s) reflected towards the camera,
5-9 Specular Reflection Green ; ie. max when NormalVector is in middle of
10-14 Specular Reflection Blue ;/LightVector and ViewDirection
15 Specular Reflection Shininess Table (0=Disable, 1=Enable)
16-20 Emission Red ;\light emitted by the polygon itself,
21-25 Emission Green ; ie. regardless of light colors/vectors,
26-30 Emission Blue ;/and no matter if any lights are enabled
31 Not used
Caution: Specular Reflection WON’T WORK when the ProjectionMatrix is rotated.
Write 32 parameter words (each 32bit word containing four 8bit entries), entries 0..3 in the first word, through entries 124..127 in the last word:
0-7 Shininess 0 (unsigned fixed-point, 0bit integer, 8bit fractional part)
8-15 Shininess 1 ("")
16-23 Shininess 2 ("")
24-31 Shininess 3 ("")
If the table is disabled (by MaterialColor1.Bit15), then reflection will act as if the table would be filled with linear increasing numbers.
In short, this command does calculate the VertexColor, based on the various light-parameters.
In detail, upon executing this command, the incoming vector is multiplied by the current Directional Matrix, the result is then applied as NormalVector (giving it the same rotation as used for the following polygon vertices).
0-9 X-Component of Normal Vector (1bit sign + 9bit fractional part)
10-19 Y-Component of Normal Vector (1bit sign + 9bit fractional part)
20-29 Z-Component of Normal Vector (1bit sign + 9bit fractional part)
30-31 Not used
Defines the Polygon’s Normal. And, does then update the Vertex Color; by recursing the View Direction, the NormalVector, the LightVector(s), and Light/Material Colors. The execution time of the Normal command varies depending on the number of enabled light(s).
Additionally to above registers, light(s) must be enabled in PolygonAttr (mind that changes to PolygonAttr aren’t applied until next Begin command). And, the Directional Matrix must be set up correctly (in MtxMode=2) for the LightVector and NormalVector commands.
The Normal vector must point “away from the polygon surface” (eg. for the floor, the Normal should point upwards). That direction is implied by the polygon vertices, however, the hardware cannot automatically calculate it, so it must be set manually with the Normal command (prior to the VTX-commands).
When using lighting, the Normal command must be re-executed after switching Lighting on/off, or after changing light/material parameters. And, of course, also before defining polygons with different orientation. Polygons with same orientation (eg. horizontal polygon surfaces) and same material color can use the same Normal. Changing the Normal per polygon gives differently colored polygons with flat surfaces, changing the Normal per vertex gives the illusion of curved surfaces.
Each light consists of parallel beams; similar to sunlight, which appears to us (due to the great distance) to consist of parallel beams, all emmitted into the same direction; towards Earth.
In reality, light is emitted into ALL directions, originated from the light source (eg. a candle), the hardware doesn’t support that type of non-parallel light. However, the light vectors can be changed per polygon, so a polygon that is located north of the light source may use different light direction than a polygon that is east of the light source.
And, of course, Light 0..3 may (and should) have different directions.
The Normal Vector and the Light Vectors should be normalized (ie. their length should be 1.0) (in practice: something like 0.99, since the registers have only fractional parts) (a length of 1.0 can cause overflows).
The functionality of the light feature is limited to reflecting light to the camera (light is not reflected to other polygons, nor does it cast shadows on other polygons). However, independently of the lighting feature, the DS hardware does allow to create shadows, see:
IF TexCoordTransformMode=2 THEN TexCoord=NormalVector*Matrix (see TexCoord)
NormalVector=NormalVector*DirectionalMatrix
VertexColor = EmissionColor
FOR i=0 to 3
IF PolygonAttrLight[i]=enabled THEN
DiffuseLevel = max(0,-(LightVector[i]*NormalVector))
ShininessLevel = max(0,(-HalfVector[i])*(NormalVector))^2
IF TableEnabled THEN ShininessLevel = ShininessTable[ShininessLevel]
;note: below processed separately for the R,G,B color components...
VertexColor = VertexColor + SpecularColor*LightColor[i]*ShininessLevel
VertexColor = VertexColor + DiffuseColor*LightColor[i]*DiffuseLevel
VertexColor = VertexColor + AmbientColor*LightColor[i]
ENDIF
NEXT i
LightVector[i] = (LightVector*DirectionalMatrix)
HalfVector[i] = (LightVector[i]+LineOfSightVector)/2
Ideally, the LineOfSightVector should point from the camera to the vertic(es), however, the vertic(es) are still unknown at time of normal command, so it is just pointing from the camera to the screen, ie.
LineOfSightVector = (0,0,-1.0)
Moreover, the LineOfSightVector should be multiplied by the Projection Matrix (so the vector would get rotated accordingly when the camera gets rotated), and, after multiplication by a scaled matrix, it’d be required to normalize the resulting vector.
However, the NDS cannot normalize vectors by hardware, and therefore, it does completely leave out the LineOfSightVector*ProjectionMatrix multiplication. So, the LineOfSightVector is always (0,0,-1.0), no matter of any camera rotation. That means,
Specular Reflection WON'T WORK when the ProjectionMatrix is rotated (!)
So, if you want to rotate the “camera” (in MTX_MODE=0), then you must instead rotate the “world” in the opposite direction (in MTX_MODE=2).
That problem applies only to Specular Reflection, ie. only if Lighting is used, and only if the Specular Material Color is nonzero.
Note on Vector*Vector multiplication: Processed as LineVector*RowVector, so the result is a number (aka scalar) (aka a matrix with only 1x1 elements), multiplying two (normalized) vectors results in: “cos(angle)=vec1*vec2”, ie. the cosine of the angle between the two vectors.
The various Normal/Light/Half/Sight vectors are only 3-dimensional (x,y,z), ie. only the upper-left 3x3 matrix elements are used on multiplications with the 4x4 DirectionalMatrix.
The DS hardware’s Light-function allows to reflect light to the camera, it does not reflect light to other polygons, and it does not cast any shadows. For shadows at fixed locations it’d be best to pre-calculate their shape and position, and to change the vertex color of the shaded polygons.
Additionally, the Shadow Polygon feature can be used to create animated shadows, ie. moved objects and variable light sources.
The software must define a Shadow Volume (ie. the region which doesn’t contain light), the hardware does then automatically draw the shadow on all pixels whose x/y/z-coordinates are inside of that region.
The Shadow Volume must be defined by several Shadow Polygons which are enclosing the shaded region. The ‘top’ of the shadow volume should be usually translated to the position of the object that casts the shadow, if the light direction changes then the shadow volume should be also rotated to match the light direction. The ‘length’ of the shadow volume should be (at least) long enough to reach from the object to the walls/floor where the shadow is to be drawn. The shadow volume must be passed TWICE to the hardware:
Set Polygon_Attr Mode=Shadow, PolygonID=00h, Back=Render, Front=Hide, Alpha=01h..1Eh, and pass the shadow volume (ie. the shadow polygons) to the geometry engine.
The Back=Render / Front=Hide setting causes the ‘rear-side’ of the shadow volume to be rendered, of course only as far as it is in front of other polygons. The Mode=Shadow / ID=00h setting causes the polygon NOT to be drawn to the Color Buffer - instead, flags are set in the Stencil Buffer (to be used in Step 2).
Simply repeat step 1, but with Polygon_Attr Mode=Shadow, PolygonID=01h..3Fh, Back=Render(what/why?), Front=Render, Alpha=01h..1Eh.
The Front=Render setting causes the ‘front-side’ of the shadow volume to be rendered, again, only as far as it is in front of other polygons. The Mode=Shadow / ID>00h setting causes the polygon to be drawn to the Color Buffer as usually, but only if the Stencil Buffer bits are zero (ie. the portion from Step 1 is excluded) (additionally, Step 2 resets the stencil bits after checking them). Moreover, the shadow is rendered only if its Polygon ID differs from the ID in the Attribute Buffer.
The Alpha=Translucent setting in Step 1 and 2 ensures that the Shadow is drawn AFTER the normal (opaque) polygons have been rendered. In Step 2 it does additionally specify the ‘intensity’ of the shadow. For normal shadows, the Vertex Color should be usually black, however, the shadow volume may be also used as ‘spotlight volume’ when using other colors.
The Mask Volume must be rendered prior to the Rendering Volume, ie. Step 1 and 2 must be performed in that order, and, to keep that order intact, Auto-sorting must have been disabled in the previous Swap_Buffers command.
The shadow volume must be rendered after the ‘target’ polygons have been rendered, for opaque targets this is done automatically (due to the translucent alpha setting; translucent polygons are always rendered last, even with auto-sort disabled).
Casting shadows on Translucent Polygons. First draw the translucent target (with update depth buffer enabled, required for the shadow z-coordinates), then draw the Shadow Mask/Rendering volumes.
Due to the updated depth buffer the shadow will be cast only on the translucent target (not on any other polygons underneath of the translucent polygon). If you want the shadow to appear on both: Draw draw the Shadow Mask/Rendering volume TWICE (once before, and once after drawing the translucent target).
The “Render only if Polygon ID differs” feature (see Step 2) allows to prevent the shadow to be cast on the object that casts the shadow (ie. the object and shadow should have the same IDs). The feature also allows to select whether overlapping shadows (with same/different IDs) are shaded once or twice.
The old Fog Enable flag in the Attribute Buffer is ANDed with the Fog Enable flag of the Shadow Polygons, this allows to exclude Fog in shaded regions.
Normally, the shadow volume should have a closed shape, ie. should have rear-sides (step 1), and corresponding front-sides (step 2) for all possible viewing angles. That is required for the shadow to be drawn correctly, and also for the Stencil Buffer to be reset to zero (in step 2, so that the stencil bits won’t disturb other shadow volumes).
Due to that, drawing errors may occur if the shadow volume’s front or rear side gets clipped by near/far clip plane.
One exception is that the volume doesn’t need a bottom-side (with a suitable volume length, the bottom may be left open, since it vanishes in the floor/walls anyways).
Specifies the texture source coordinates within the texture bitmap which are to be associated with the next vertex.
Parameter 1, Bit 0-15 S-Coordinate (X-Coordinate in Texture Source)
Parameter 1, Bit 16-31 T-Coordinate (Y-Coordinate in Texture Source)
Both values are 1bit sign + 11bit integer + 4bit fractional part.
A value of 1.0 (=1 SHL 4) equals to one Texel.
With Position 0.0 , 0.0 drawing starts from upperleft of the Texture.
With positive offsets, drawing origin starts more “within” the texture.
With negative offsets, drawing starts “before” the texture.
“When texture mapping, the Geometry Engine works faster if you issue commands in the order TexCoord -> Normal -> Vertex.”
0-15 Texture VRAM Offset div 8 (0..FFFFh -> 512K RAM in Slot 0,1,2,3)
(VRAM must be allocated as Texture data, see Memory Control chapter)
16 Repeat in S Direction (0=Clamp Texture, 1=Repeat Texture)
17 Repeat in T Direction (0=Clamp Texture, 1=Repeat Texture)
18 Flip in S Direction (0=No, 1=Flip each 2nd Texture) (requires Repeat)
19 Flip in T Direction (0=No, 1=Flip each 2nd Texture) (requires Repeat)
20-22 Texture S-Size (for N=0..7: Size=(8 SHL N); ie. 8..1024 texels)
23-25 Texture T-Size (for N=0..7: Size=(8 SHL N); ie. 8..1024 texels)
26-28 Texture Format (0..7, see below)
29 Color 0 of 4/16/256-Color Palettes (0=Displayed, 1=Made Transparent)
30-31 Texture Coordinates Transformation Mode (0..3, see below)
Texture Formats:
0 No Texture
1 A3I5 Translucent Texture
2 4-Color Palette Texture
3 16-Color Palette Texture
4 256-Color Palette Texture
5 4x4-Texel Compressed Texture
6 A5I3 Translucent Texture
7 Direct Texture
Texture Coordinates Transformation Modes:
0 Do not Transform texture coordinates
1 TexCoord source
2 Normal source
3 Vertex source
The S-Direction equals to the horizontal direction of the source bitmap.
The T-Direction, T-repeat, and T-flip are the same in vertical direction.
For a “/” shaped texture, the S-clamp, S-repeat, and S-flip look like so:
Clamp _____ Repeat Repeat+Flip
_____/ /////////// /\/\/\/\/\/
With “Clamp”, the texture coordinates are clipped to MinMax(0,Size-1), so the texels at the edges of the texture bitmap are repeated (to avoid that effect, fill the bitmap edges by texels with alpha=0, so they become invisible).
0-12 Palette Base Address (div8 or div10h, see below)
(Not used for Texture Format 7: Direct Color Texture)
(0..FFF8h/8 for Texture Format 2: ie. 4-color-palette Texture)
(0..17FF0h/10h for all other Texture formats)
13-31 Not used
The palette data occupies 16bit per color, Bit0-4: Red, Bit5-9: Green, Bit10-14: Blue, Bit15: Not used.
(VRAM must be allocated as Texture Palette, there can be up to 6 Slots allocated, ie. the addressable 18000h bytes, see Memory Control chapter)
Can be issued per polygon (except within polygon strips).
Each Texel occupies 2bit, the first Texel is located in LSBs of 1st byte.
In this format, the Palette Base is specified in 8-byte steps; all other formats use 16-byte steps (see PLTT_BASE register).
Each Texel occupies 4bit, the 1st Texel is located in LSBs of 1st byte.
Each Texel occupies 8bit, the 1st Texel is located in 1st byte.
Each Texel occupies 16bit, the 1st Texel is located in 1st halfword.
Bit0-4: Red, Bit5-9: Green, Bit10-14: Blue, Bit15: Alpha
Each Texel occupies 8bit, the 1st Texel is located in 1st byte.
Bit0-4: Color Index (0..31) of a 32-color Palette
Bit5-7: Alpha (0..7; 0=Transparent, 7=Solid)
The 3bit Alpha value (0..7) is internally expanded into a 5bit Alpha value (0..31) as follows: Alpha=(Alpha*4)+(Alpha/2).
Each Texel occupies 8bit, the 1st Texel is located in 1st byte.
Bit0-2: Color Index (0..7) of a 8-color Palette
Bit3-7: Alpha (0..31; 0=Transparent, 31=Solid)
Consists of 4x4 Texel blocks in Slot 0 or 2, 32bit per block, 2bit per Texel,
Bit0-7 Upper 4-Texel row (LSB=first/left-most Texel)
Bit8-15 Next 4-Texel row ("")
Bit16-23 Next 4-Texel row ("")
Bit24-31 Lower 4-Texel row ("")
Additional Palette Index Data for each 4x4 Texel Block is located in Slot 1,
Bit0-13 Palette Offset in 4-byte steps; Addr=(PLTT_BASE*10h)+(Offset*4)
Bit14-15 Transparent/Interpolation Mode (0..3, see below)
whereas, the Slot 1 offset is related to above Slot 0 or 2 offset,
slot1_addr = slot0_addr / 2 ;lower 64K of Slot1 assoc to Slot0
slot1_addr = slot2_addr / 2 + 10000h ;upper 64K of Slot1 assoc to Slot2
The 2bit Texel values (0..3) are intepreted depending on the Mode (0..3),
Texel Mode 0 Mode 1 Mode 2 Mode 3
0 Color 0 Color0 Color 0 Color 0
1 Color 1 Color1 Color 1 Color 1
2 Color 2 (Color0+Color1)/2 Color 2 (Color0*5+Color1*3)/8
3 Transparent Transparent Color 3 (Color0*3+Color1*5)/8
Mode 1 and 3 are using only 2 Palette Colors (which requires only half as much Palette memory), the 3rd (and 4th) Texel Colors are automatically set to above values (eg. to gray-shades if color 0 and 1 are black and white).
Note: The maximum size for 4x4-Texel Compressed Textures is 1024x512 or 512x1024 (which are both occupying the whole 128K in slot 0 or 2, plus 64K in slot1), a larger size of 1024x1024 cannot be used because of the gap between slot 0 and 2.
For textured polygons, a texture coordinate must be associated with each vertex of the polygon. The coordinates (S,T) are defined by TEXCOORD command (typically issued prior to each VTX command), and can be optionally automatically transformed, by the Transformation Mode selected in TEXIMAGE_PARAM register.
Although the texture matrix is 4x4, with values m[0..15], only the left two columns of this matrix are actually used. In Mode 2 and 3, the bottom row of the matrix is replaced by S and T values from most recent TEXCOORD command.
The values are set upon executing the TEXCOORD command,
( S' T' ) = ( S T )
Simple coordinate association, without using the Texture Matrix at all.
The values are calculated upon executing the TEXCOORD command,
| m[0] m[1] |
( S' T' ) = ( S T 1/16 1/16 ) * | m[4] m[5] |
| m[8] m[9] |
| m[12] m[13] |
Can be used to produce a simple texture scrolling, rotation, or scaling, by setting a translate, rotate, or scale matrix for the texture matrix.
The values are calculated upon executing the NORMAL command,
| m[0] m[1] |
( S' T' ) = ( Nx Ny Nz 1.0 ) * | m[4] m[5] |
| m[8] m[9] |
| S T |
Can be used to produce spherical reflection mapping by setting the texture matrix to the current directional vector matrix, multiplied by a scaling matrix that expands the directional vector space from -1.0..+1.0 to one half of the texture size. For that purpose, translate the origin of the texture coordinate to the center of the spherical texture by using TexCoord command (spherical texture means a bitmap that contains some circle-shaped image).
The values are calculated upon executing any VTX commands,
| m[0] m[1] |
( S' T' ) = ( Vx Vy Vz 1.0 ) * | m[4] m[5] |
| m[8] m[9] |
| S T |
Can be used to produce texture scrolls dependent on the View coordinates by copying the current position coordinate matrix into the texture matrix. For example, the PositionMatrix can be obtained via CLIPMTX_RESULT (see there for details), and that values can be then manually copied to the TextureMatrix.
Matrix m[..] 1+19+12 (32bit)
Vertex Vx,Vy,Vz 1+3+12 (16bit)
Normal Nx,Ny,Nz 1+0+9 (10bit)
Constant 1.0 0+1+0 (1bit)
Constant 1/16 0+0+4 (4bit)
TexCoord S,T 1+11+4 (16bit)
Result S',T' 1+11+4 (16bit) <-------- clipped to that size !
Observe that the S’,T’ values are clipped to 16bit size. Ie. after the Vector*Matrix calaction, the result is shifted right (to make it having a 4bit fraction), and the value is then masked to 16bit size.
Polygon pixels consist of a Vertex Color, and of Texture Colors.
These colors can be blended as described below. Or, to use only either one:
To use only the Vertex Color: Select No Texture in TEXIMAGE_PARAM.
To use only the Texture Color: Select Modulation Mode and Alpha=31 in POLYGON_ATTR, and set COLOR to 7FFFh (white), or to gray values (to decrease brightness of the texture color).
The Vertex Color (Rv,Gv,Bv) can be changed per Vertex (either by Color, Normal, or Material0 command), pixels between vertices are shaded to medium values of the surrounding vertices. The Vertex Alpha (Av), can be changed only per polygon (by PolygonAttr command).
The Texture Colors (Rt,Gt,Bt), and Alpha value (At), are defined by the Texture Bitmap. For formats without Alpha value, assume At=31 (solid), and for formats with 1bit Alpha assume At=A*31.
In Toon/Highlight Shading Mode, the red component of the Vertex Color (Rv) is mis-used as an index in the Shading Table, ie. Rv is used to read Shading Colors (Rs,Gs,Bs) from the table; the green and blue components of the Vertex Color (Gv,Bv) are unused in this mode. The Vertex Alpha (Av) is kept used.
Shading is used in Polygon Mode 2, whether it is Toon or Highlight Shading is selected in DISP3DCNT; this is a per-frame selection, so only either one can be used.
R = ((Rt+1)*(Rv+1)-1)/64
G = ((Gt+1)*(Gv+1)-1)/64
B = ((Bt+1)*(Bv+1)-1)/64
A = ((At+1)*(Av+1)-1)/64
The multiplication result is decreased intensity (unless both factors are 63).
R = (Rt*At + Rv*(63-At))/64 ;except, when At=0: R=Rv, when At=31: R=Rt
G = (Gt*At + Gv*(63-At))/64 ;except, when At=0: G=Gv, when At=31: G=Gt
B = (Bt*At + Bv*(63-At))/64 ;except, when At=0: B=Bv, when At=31: B=Bt
A = Av
The At value is used (only) as ratio for Texture color vs Vertex Color.
The vertex color Red component (Rv) is used as an index in the toon table.
R = ((Rt+1)*(Rs+1)-1)/64 ;Rs=ToonTableRed[Rv]
G = ((Gt+1)*(Gs+1)-1)/64 ;Gs=ToonTableGreen[Rv]
B = ((Bt+1)*(Bs+1)-1)/64 ;Bs=ToonTableBlue[Rv]
A = ((At+1)*(Av+1)-1)/64
This is same as Modulation Mode, but using Rs,Gs,Bs instead Rv,Gv,Bv.
R = ((Rt+1)*(Rs+1)-1)/64+Rs ;truncated to MAX=63
G = ((Gt+1)*(Gs+1)-1)/64+Gs ;truncated to MAX=63
B = ((Bt+1)*(Bs+1)-1)/64+Bs ;truncated to MAX=63
A = ((At+1)*(Av+1)-1)/64
Same as Toon Shading, with additional addition offset, the addition may increase the intensity, however, it may also change the hue of the color.
Above formulas are for 6bit RGBA values, ie. 5bit values internally expanded to 6bit as such: IF X>0 THEN X=X*2+1.
Although textures are normally containing “pictures”, in some cases it makes sense to use “blank” textures that are filled with a single color:
Wire-frame polygons are always having Av=31, however, they can be made transparent by using Translucent Textures (ie. A5I3 or A3I5 formats) with At<31.
In Toon/Highlight shading modes, the Vertex Color is mis-used as table index, however, Toon/Highlight shading can be used on uni-colored textures, which is more or less the same as using Toon/Highlight shading on uni-colored Vertex-colors.
This 64-byte region contains the 32 toon colors (16bit per color), used for both Toon and Highlight Shading. In both modes, the Red (R) component of the RGBA vertex color is mis-used as index to obtain the new RGB value from the toon table, vertex Alpha (A) is kept used as is.
Bit0-4: Red, Bit5-9: Green, Bit10-14: Blue, Bit15: Not Used
Shading can be enabled (per polygon) in Polygon_Attr, whether it is Toon or Highlight Shading is set (per frame) in DISP3DCNT. For more info on shading, see:
This 16-byte region contains the 8 edge colors (16bit per color), Edge Color 0 is used for Polygon ID 00h..07h, Color 1 for ID 08h..0Fh, and so on.
Bit0-4: Red, Bit5-9: Green, Bit10-14: Blue, Bit15: Not Used
Edge Marking allows to mark the edges of an object (whose polygons all have the same ID) in a wire-frame style. Edge Marking can be enabled (per frame) in DISP3DCNT. When enabled, the polygon edges are drawn at the edge color, but only if the old ID value in the Attribute Buffer is different than the Polygon ID of the new polygon, so no edges are drawn between connected or overlapping polygons with same ID values.
Edge Marking is applied ONLY to opaque polygons (including wire-frames).
Edge Marking increases the size of opaque polygons (see notes below).
Edge Marking doesn’t work very well with Anti-Aliasing (see Anti-Aliasing).
Technically, when rendering a polygon, it’s edges (ie. the wire-frame region) are flagged as possible-edges (but it’s still rendered normally, without using the edge-color). Once when all opaque polygons (*) have been rendered, the edge color is applied to these flagged pixels, under following conditions: At least one of the four surrounding pixels (up, down, left, right) must have different polygon_id than the edge, and, the edge depth must be LESS than the depth of that surrounding pixel (ie. no edges are rendered if the depth is GREATER or EQUAL, even if the polygon_id differs). At the screen borders, edges seem to be rendered in respect to the rear-plane’s polygon_id entry (see Port 4000350h).
(*) Actually, edge-marking is reportedly performed not until all opaque AND translucent polygons have been rendered. That brings up some effects/problems when edges are covered by translucent polys: The edge-color is probably drawn as is (ie. it’ll overwrite the translucent color, rather than being blended with the translucent color). And, any translucent polygons that do update the depth buffer will cause total edge-marking malfunction (since edge-marking involves the comparision of the current/surrounding pixel’s depth values).
Fog can be used to let more distant polygons to disappear in foggy grayness (or in darkness, or other color). This is particulary useful to “hide” the far clip plane. Fog can be enabled in DISP3DCNT.Bit7, moreover, when enabled, it can be activated or deactivated per polygon (POLYGON_ATTR.Bit15), and per Rear-plane (see there).
0-4 Fog Color, Red ;\
5-9 Fog Color, Green ; used only when DISP3DCNT.Bit6 is zero
10-14 Fog Color, Blue ;/
15 Not used
16-20 Fog Alpha ;-used no matter of DISP3DCNT.Bit6
21-31 Not used
Whether or not fog is applied to a pixel depends on the Fog flag in the framebuffer, the initial value of that flag can be defined in the rear-plane. When rendering opaque pixels, the framebuffer’s fog flag gets replaced by PolygonAttr.Bit15. When rendering translucent pixels, the old flag in the framebuffer gets ANDed with PolygonAttr.Bit15.
0-14 Fog Offset (Unsigned) (0..7FFFh)
15-31 Not used
FogDepthBoundary[0..31] (for FogDensity[0..31]) are defined as:
FogDepthBoundary[n] = FOG_OFFSET + FOG_STEP*(n+1) ;with n = 0..31
Whereas FOG_STEP is derived from the FOG_SHIFT value in DISP3DCNT.Bit8-11 (FOG_STEP=400h shr FOG_SHIFT) (normally FOG_SHIFT should be 0..10 (bigger shift amounts of 11..15 would cause FOG_STEP to become zero, so only Density[0] and Density[31] would be used).
The meaning of the depth values depends on whether z-values or w-values are stored in the framebuffer (see SwapBuffers.Bit1).
For translucent polygons, the depth value (and therefore: the amount of fog) depends on the depth update bit (see PolygonAttr.Bit11).
This 32-byte region contains FogDensity[0..31] (used at FogDepthBoundary[n]),
0-6 Fog Density (00h..7Fh = None..Full) (usually increasing values)
7 Not used
FogDensity[0] is used for all pixels closer than FogDepthBoundary[0], FogDensity[31] is used for all pixels more distant than FogDepthBoundary[0].
Density is linear interpolated for pixels that are between two Density depth boundaries. The formula for Fog Blending is:
FrameBuffer[R] = (FogColor[R]*Density + FrameBuffer[R]*(128-Density)) / 128
FrameBuffer[G] = (FogColor[G]*Density + FrameBuffer[G]*(128-Density)) / 128
FrameBuffer[B] = (FogColor[B]*Density + FrameBuffer[B]*(128-Density)) / 128
FrameBuffer[A] = (FogColor[A]*Density + FrameBuffer[A]*(128-Density)) / 128
If DISP3DCNT.Bit6 is set (=Alpha Only), then only FrameBuffer[A] is updated, and FrameBuffer[RGB] are kepth unchanged. Density=127 is handled as if Density=128.
Fog Glitch: The fog_alpha value appears to be ignored (treated as fog_alpha=1Fh) in the region up to the first density boundary. However, normally that value will be multiplied by zero (assumung that density[0] is usually zero), so you won’t ever notice that hardware glitch.
Alpha-Blending occurs for pixels of translucent polygons,
FrameBuf[R] = (Poly[R]*(Poly[A]+1) + FrameBuf[R]*(31-(Poly[A])) / 32
FrameBuf[G] = (Poly[G]*(Poly[A]+1) + FrameBuf[G]*(31-(Poly[A])) / 32
FrameBuf[B] = (Poly[B]*(Poly[A]+1) + FrameBuf[B]*(31-(Poly[A])) / 32
FrameBuf[A] = max(Poly[A],FrameBuf[A])
There are three situations in which Alpha-Blending is bypassed (the old Framebuf[R,G,B,A] value is then simply overwritten by Poly[R,G,B,A]):
1) Alpha-Blending is disabled (DISP3DCNT.Bit3=0)
2) The polygon pixel is opaque (Poly[A]=31)
3) The old framebuffer value is totally transparent (FrameBuf[A]=0)
The third case can happen if the rear-plane was initialized with Alpha=0, which causes the polygon not to be blended with the rear-plane (which may give better results when subsequently blending the 3D layer with the 2D engine).
Note: Totally transparent pixels (with Poly[A]=0) are not rendered (ie. neither FrameBuf[R,G,B,A] nor FrameBuf[Depth,Fog,PolyID,etc.] are updated.
Anti-Aliasing can be enabled in DISP3DCNT, when enabled, the edges of opaque polygons will be anti-aliased (ie. the pixels at the edges may become translucent).
Anti-Aliasing is not applied on translucent polygons. And, Anti-Aliasing is not applied on the interiors of the poylgons (eg. an 8x8 chessboard texture will be anti-aliased only at the board edges, not at the edges of the 64 fields).
Anti-Aliasing is (accidently) applied to opaque 1dot polygongs, line-segments and wire-frames (which results in dirty lines with missing pixels, 1dot polys become totally invisible), workaround is to use translucent dots, lines and wires (eg. with alpha=30).
Anti-Aliasing is (correctly) not applied to edges of Edge-Marked polygons, in that special case even opaque line-segments and wire-frames are working even if anti-aliasing is enabled (provided that they are edge-marked, ie. if their polygon ID differs from the framebuffer’s ID).
Anti-Aliasing is (accidently) making the edges of Edge-Marked polygons translucent (with alpha=16 or so?), that reduces the contrast of the edge colors. Moreover, if two of these translucent edges do overlap, then they are blended twice (even if they have the same polygon_id, and even if the depth_update bit in polygon_attr is set; both should normally prevent double-blending), that scatters the brightness of such edges.
In some cases, the NDS hardware doesn’t render the lower/right edges of certain polygons. That feature reduces rendering load, and, when rendering connected polygons (eg. strips), then it’d be unnecessary to render that edges (since they’d overlap with the upper/left edges of the other polygon). On the contrary, if there’s no connected polygon displayed, then the polygon may appear smaller than expected. Small polygons with excluded edges are:
Opaque polygons (except wire-frames) without Edge-Marking and Anti-Aliasing,
and, all polygons with vertical right-edges (except line-segments).
Plus, Translucent Polys when Alpha-Blending is disabled in DISP3DCNT.Bit3.
All other polygons are rendered at full size with all edges included (except vertical right edges). Note: To disable the small-polygon feature, you can enable edge-marking (which does increase the polygon size, even if no edges are drawn, ie. even if all polys do have the same ID).
Bit 30-31 are R/W. Writing “1” to Bit15 does reset the Error Flag (Bit15), and additionally resets the Projection Stack Pointer (Bit13), and probably (?) also the Texture Stack Pointer. All other GXSTAT bits are read-only.
0 BoxTest,PositionTest,VectorTest Busy (0=Ready, 1=Busy)
1 BoxTest Result (0=All Outside View, 1=Parts or Fully Inside View)
2-7 Not used
8-12 Position & Vector Matrix Stack Level (0..31) (lower 5bit of 6bit value)
13 Projection Matrix Stack Level (0..1)
14 Matrix Stack Busy (0=No, 1=Yes; Currently executing a Push/Pop command)
15 Matrix Stack Overflow/Underflow Error (0=No, 1=Error/Acknowledge/Reset)
16-24 Number of 40bit-entries in Command FIFO (0..256)
(24) Command FIFO Full (MSB of above) (0=No, 1=Yes; Full)
25 Command FIFO Less Than Half Full (0=No, 1=Yes; Less than Half-full)
26 Command FIFO Empty (0=No, 1=Yes; Empty)
27 Geometry Engine Busy (0=No, 1=Yes; Busy; Commands are executing)
28-29 Not used
30-31 Command FIFO IRQ (0=Never, 1=Less than half full, 2=Empty, 3=Reserved)
When GXFIFO IRQ is enabled (setting 1 or 2), the IRQ flag (IF.Bit21) is set while and as long as the IRQ condition is true (and attempts to acknowledge the IRQ by writing to IF.Bit21 have no effect). So that, the IRQ handler must either fill the FIFO, or disable the IRQ (setting 0), BEFORE trying to acknowledge the IRQ.
0-11 Number of Polygons currently stored in Polygon List RAM (0..2048)
12-15 Not used
16-28 Number of Vertices currently stored in Vertex RAM (0..6144)
13-15 Not used
If a SwapBuffers command has been sent, then the counters are reset 10 cycles (at 33.51MHz clock) after next VBlank.
Rendering starts in scanline 214, the rendered lines are stored in a buffer that can hold up to 48 scanlines. The actual screen output begins after scanline 262, the lines are then read from the buffer and sent to the display. Simultaneously, the rendering engine keeps writing new lines to the buffer (ideally at the same speed than display output, so the buffer would always contain 48 pre-calculated lines).
0-5 Minimum Number (minus 2) of buffered lines in previous frame (0..46)
6-31 Not used
If rendering becomes slower than the display output, then the number of buffered lines decreases. Smaller values in RDLINES indicate that additional load to the rendering engine may cause buffer underflows in further frames, if so, the program should reduce the number of polygons to avoid display glitches.
Even if RDLINES becomes zero, it doesn’t indicate whether actual buffer underflows have occured or not (underflows are indicated in DISP3DCNT Bit12).
The BoxTest result indicates if one or more of the 6 faces of the box are fully or parts of inside of the view volume. Can be used to reduce unnecessary overload, ie. if the result is false, then the program can skip drawing of objects which are inside of the box.
BoxTest verifies only if the faces of the box are inside view volume, and so, it will return false if the whole view volume is located inside of the box (still objects inside of the box may be inside of view).
Parameter 1, Bit 0-15 X-Coordinate
Parameter 1, Bit 16-31 Y-Coordinate
Parameter 2, Bit 0-15 Z-Coordinate
Parameter 2, Bit 16-31 Width (presumably: X-Offset?)
Parameter 3, Bit 0-15 Height (presumably: Y-Offset?)
Parameter 3, Bit 16-31 Depth (presumably: Z-Offset?)
All values are 1bit sign, 3bit integer, 12bit fractional part
The result of the “coordinate+offset” additions should not overflow 16bit vertex coordinate range (1bit sign, 3bit integer, 12bit fraction).
Before using BoxTest, be sure that far-plane-intersecting & 1-dot polygons are enabled, if they aren’t: Send the PolygonAttr command (with bit12,13 set to enable them), followed by dummy Begin and End commands (required to apply the new PolygonAttr settings). BoxTest should not be issued within Begin/End.
After sending the BoxTest command, wait until GXSTAT.Bit0 indicates Ready, then read the result from GXSTAT.Bit1.
Parameter 1, Bit 0-15 X-Coordinate
Parameter 1, Bit 16-31 Y-Coordinate
Parameter 2, Bit 0-15 Z-Coordinate
Parameter 2, Bit 16-31 Not used
All values are 1bit sign, 3bit integer, 12bit fractional part.
Multiplies the specified line-vector (x,y,z,1) by the clip coordinate matrix.
After sending the command, wait until GXSTAT.Bit0 indicates Ready, then read the result from POS_RESULT registers. POS_TEST can be issued anywhere (except within polygon strips, huh?).
Caution: POS_TEST overwrites the internal VTX registers, so the next vertex should be <fully> defined by VTX_10 or VTX_16, otherwise, when using VTX_XY, VTX_XZ, VTX_YZ, or VTX_DIFF, then the new vertex will be relative to the POS_TEST coordinates (rather than to the previous vertex).
This 16-byte region (4 words) contains the resulting clip coordinates (x,y,z,w) from the POS_TEST command. Each value is 1bit sign, 19bit integer, 12bit fractional part.
Parameter 1, Bit 0-9 X-Component
Parameter 1, Bit 10-19 Y-Component
Parameter 1, Bit 20-29 Z-Component
Parameter 1, Bit 30-31 Not used
All values are 1bit sign, 9bit fractional part.
Multiplies the specified line-vector (x,y,z,0) by the directional vector matrix. Similar as for the NORMAL command, it does require Matrix Mode 2 (ie. Position & Vector Simultaneous Set mode).
After sending the command, wait until GXSTAT.Bit0 indicates Ready, then read the result (“the directional vector in the View coordinate space”) from VEC_RESULT registers.
This 6-byte region (3 halfwords) contains the resulting vector (x,y,z) from the VEC_TEST command. Each value is 4bit sign, 0bit integer, 12bit fractional part. The 4bit sign is either 0000b (positive) or 1111b (negative).
There is no integer part, so values >=1.0 or <-1.0 will cause overflows.
(Eg. +1.0 aka 1000h will be returned as -1.0 aka F000h due to overflow and sign-expansion).
Other docs seem to refer to this as Clear-plane, rather than Rear-plane, anyways, the plane can be an image, so it isn’t always “cleared”.
The view order is as such:
--> 2D Layers --> 3D Polygons --> 3D Rear-plane --> 2D Layers --> 2D Backdrop
The rear-plane can be disabled (by making it transparent; alpha=0), so that the 2D layers become visible as background.
2D layers can be moved in front of, or behind the 3D layer-group (which is represented as BG0 to the 2D Engine), 2D layers behind BG0 can be used instead of, or additionally to the rear-plane.
The rear-plane can be initialized via below two registers (so all pixels in the plane have the same colors and attributes), this method is used when DISP3DCNT.14 is zero:
0-4 Clear Color, Red
5-9 Clear Color, Green
10-14 Clear Color, Blue
15 Fog (enables Fog to the rear-plane) (doesn't affect Fog of polygons)
16-20 Alpha
21-23 Not used
24-29 Clear Polygon ID (affects edge-marking, at the screen-edges?)
30-31 Not used
0-14 Clear Depth (0..7FFFh) (usually 7FFFh = most distant)
15 Not used
16-31 See Port 4000356h, CLRIMAGE_OFFSET
The 15bit Depth is expanded to 24bit as “X=(X*200h)+((X+1)/8000h)*1FFh”.
Alternately, the rear-plane can be initialized by bitmap data (allowing to assign different colors & attributes to each pixel), this method is used when DISP3DCNT.14 is set:
Consists of two bitmaps (one with color data, one with depth data), each containing 256x256 16bit entries, and so, each occupying a whole 128K slot,
Rear Color Bitmap (located in Texture Slot 2)
0-4 Clear Color, Red
5-9 Clear Color, Green
10-14 Clear Color, Blue
15 Alpha (0=Transparent, 1=Solid) (equivalent to 5bit-alpha 0 and 31)
Rear Depth Bitmap (located in Texture Slot 3)
0-14 Clear Depth, expanded to 24bit as X=(X*200h)+((X+1)/8000h)*1FFh
15 Clear Fog (Initial fog enable value)
This method requires VRAM to be allocated to Texture Slot 2 and 3 (see Memory Control chapter). Of course, in that case the VRAM is used as Rear-plane, and cannot be used for Textures.
The bitmap method is restricted to 1bit alpha values (the register-method allows to use a 5bit alpha value).
The Clear Polygon ID is kept defined in the CLEAR_COLOR register, even in bitmap mode.
The visible portion of the bitmap is 256x192 pixels (regardless of the viewport setting, which is used only for polygon clipping). Internally, the bitmap is 256x256 pixels, so the bottom-most 64 rows are usually offscreen, unless scrolling is used to move them into view.
Bit0-7 X-Offset (0..255; 0=upper row of bitmap)
Bit8-14 Y-Offset (0..255; 0=left column of bitmap)
The bitmap wraps to the upper/left edges when exceeding the lower/right edges.
The final 3D image (consisting of polygons and rear-plane) is passed to 2D Engine A as BG0 layer (provided that DISPCNT is configured to use 3D as BG0).
The BG0HOFS register (4000010h) can be used the scroll the 3D layer horizontally, the scroll region is 512 pixels, consisting of 256 pixels for the 3D image, followed by 256 transparent pixels, and then wrapped to the 3D image again. Vertical scrolling (and rotation/scaling) cannot be used on the 3D layer.
The lower 2bit of the BG0CNT register (4000008h) control the priority relative to other BGs and OBJs, so the 3D layer can be in front of or behind 2D layers. All other bits in BG0CNT have no effect on 3D, namely, mosaic cannot be used on the 3D layer.
Special Effects Registers (4000050h..54h) can be used as such:
Brightness up/down with BG0 as 1st Target via EVY (as for 2D)
Blending with BG0 as 2nd Target via EVA/EVB (as for 2D)
Blending with BG0 as 1st Target via 3D Alpha-values (unlike as for 2D)
The latter method probably (?) uses per-pixel 3D alpha values as such: EVA=A/2, and EVB=16-A/2, without using the EVA/EVB settings in 4000052h.
Window Feature (4000040h..4Bh) can be used as for 2D.
“If the 3D screen has highest priority, then alpha-blending is always enabled, regardless of the Window Control register’s color effect enable flag [ie. regardless of Bit5 of WIN0IN, WIN1IN, WINOBJ, WINOUT registers]”… not sure if that is true, and if it superseedes the effect selection in Port 4000050h…?
The DS contains 16 hardware sound channels.
The console contains two speakers, arranged left and right of the upper screen, and so, provides stereo sound even without using the headphone socket.
When restoring power supply to the sound circuit, do not output any sound during the first 15 milliseconds.
Each of the 16 sound channels occopies 16 bytes in the I/O region, starting with channel 0 at 4000400h..400040Fh, up to channel 15 at 40004F0h..40004FFh.
Bit0-6 Volume Mul (0..127=silent..loud)
Bit7 Not used (always zero)
Bit8-9 Volume Div (0=Normal, 1=Div2, 2=Div4, 3=Div16)
Bit10-14 Not used (always zero)
Bit15 Hold (0=Normal, 1=Hold last sample after one-shot sound)
Bit16-22 Panning (0..127=left..right) (64=half volume on both speakers)
Bit23 Not used (always zero)
Bit24-26 Wave Duty (0..7) ;HIGH=(N+1)*12.5%, LOW=(7-N)*12.5% (PSG only)
Bit27-28 Repeat Mode (0=Manual, 1=Loop Infinite, 2=One-Shot, 3=Prohibited)
Bit29-30 Format (0=PCM8, 1=PCM16, 2=IMA-ADPCM, 3=PSG/Noise)
Bit31 Start/Status (0=Stop, 1=Start/Busy)
All channels support ADPCM/PCM formats, PSG rectangular wave can be used only on channels 8..13, and white noise only on channels 14..15.
Bit0-26 Source Address (must be word aligned, bit0-1 are always zero)
Bit27-31 Not used
Bit0-15 Timer Value, Sample frequency, timerval=-(33513982Hz/2)/freq
The PSG Duty Cycles are composed of eight “samples”, and so, the frequency for Rectangular Wave is 1/8th of the selected sample frequency.
For PSG Noise, the noise frequency is equal to the sample frequency.
Bit0-15 Loop Start, Sample loop start position
(counted in words, ie. N*4 bytes)
The number of samples for N words is 4*N PCM8 samples, 2*N PCM16 samples, or 8*(N-1) ADPCM samples (the first word containing the ADPCM header). The Sound Length is not used in PSG mode.
Bit0-21 Sound length (counted in words, ie. N*4 bytes)
Bit22-31 Not used
Minimum length (the sum of PNT+LEN) is 4 words (16 bytes), smaller values (0..3 words) are causing hang-ups (busy bit remains set infinite, but no sound output occurs).
In One-shot mode, the sound length is the sum of (PNT+LEN).
In Looped mode, the length is (1*PNT+Infinite*LEN), ie. the first part (PNT) is played once, the second part (LEN) is repeated infinitely.
Bit0-6 Master Volume (0..127=silent..loud)
Bit7 Not used (always zero)
Bit8-9 Left Output from (0=Left Mixer, 1=Ch1, 2=Ch3, 3=Ch1+Ch3)
Bit10-11 Right Output from (0=Right Mixer, 1=Ch1, 2=Ch3, 3=Ch1+Ch3)
Bit12 Output Ch1 to Mixer (0=Yes, 1=No) (both Left/Right)
Bit13 Output Ch3 to Mixer (0=Yes, 1=No) (both Left/Right)
Bit14 Not used (always zero)
Bit15 Master Enable (0=Disable, 1=Enable)
Bit16-31 Not used (always zero)
Bit0-9 Sound Bias (0..3FFh, usually 200h)
Bit10-31 Not used (always zero)
After applying the master volume, the signed left/right audio signals are in range -200h..+1FFh (with medium level zero), the Bias value is then added to convert the signed numbers into unsigned values (with medium level 200h).
BIAS output is always enabled, even when Master Enable (SOUNDCNT.15) is off.
The sampling frequency of the mixer is 1.04876 MHz with an amplitude resolution of 24 bits, but the sampling frequency after mixing with PWM modulation is 32.768 kHz with an amplitude resolution of 10 bits.
The DS contains 2 built-in sound capture devices that can capture output waveform data to memory.
Sound capture 0 can capture output from left-mixer or output from channel 0.
Sound capture 1 can capture output from right-mixer or output from channel 2.
Bit0 Control of Associated Sound Channels (ANDed with Bit7)
SNDCAP0CNT: Output Sound Channel 1 (0=As such, 1=Add to Channel 0)
SNDCAP1CNT: Output Sound Channel 3 (0=As such, 1=Add to Channel 2)
Caution: Addition mode works only if BOTH Bit0 and Bit7 are set.
Bit1 Capture Source Selection
SNDCAP0CNT: Capture 0 Source (0=Left Mixer, 1=Channel 0/Bugged)
SNDCAP1CNT: Capture 1 Source (0=Right Mixer, 1=Channel 2/Bugged)
Bit2 Capture Repeat (0=Loop, 1=One-shot)
Bit3 Capture Format (0=PCM16, 1=PCM8)
Bit4-6 Not used (always zero)
Bit7 Capture Start/Status (0=Stop, 1=Start/Busy)
Bit0-26 Destination address (word aligned, bit0-1 are always zero)
Bit27-31 Not used (always zero)
Capture start address (also used as re-start address for looped capture).
Bit0-15 Buffer length (1..FFFFh words) (ie. N*4 bytes)
Bit16-31 Not used
Minimum length is 1 word (attempts to use 0 words are interpreted as 1 word).
There are no separate capture frequency registers, instead, the sample frequency of Channel 1/3 is shared for Capture 0/1. These channels are intended to output the captured data, so it makes sense that both capture and sound output use the same frequency.
For Capture 0, a=0, b=1, x=0.
For Capture 1, a=2, b=3, x=1.
The NDS contains two hardware bugs which do occur when capturing data from ch(a) (SNDCAPxCNT.Bit1=1), if so, either bug occurs depending on whether ch(a)+ch(b) addition is enabled or disabled (SNDCAPxCNT.Bit0).
1) Both Negative Bug - SNDCAPxCNT Bit1=1, Bit0=0 (addition disabled)
Capture data is accidently set to -8000h if ch(a) and ch(b) are both <0.
Otherwise the correct capture result is returned, ie. plain ch(a) data,
not being affected by ch(b) (since addition is disabled).
Workaround: Ensure that ch(a) and/or ch(b) are >=0 (or disabled).
2) Overflow Bug - SNDCAPxCNT Bit1=1, Bit0=1 (addition enabled)
In this mode, Capture data isn't clipped to MinMax(-8000h,+7FFFh),
instead, it is ANDed with FFFFh, so the sign bit is lost if the
addition result ch(a)+ch(b) is less/greater than -8000h/+7FFFh.
Workaround: Reduce ch(a)/ch(b) volume or data to avoid overflows.
These bugs occur only for capture (speaker output remains intact), and they occur only when capturing ch(a) (capturing mixer-output works flawless).
The ch(a)+ch(b) addition unit has 2 outputs, with slightly different results:
Addition mode can be used only if the <corresponding> capture unit is enabled, ie. if SNDCAPxCNT (Bit0 AND Bit7)=1. If so, addition affects both mixers (and so, may also affect the <other> capture unit if it reads from mixer).
_____
Ch0.L ------------->| | .------------------------------> to Capture 0
___ | | | ___
Ch1.L ---o->|Sel|-->| | | Ch0..Ch15 | |
| |___| |Left |--o---------------->| |
Ch2.L ---|--------->|Mixer| |Sel| ______ ____
| ___ | | Ch1 | | |Master| |Add |
Ch3.L -o-|->|Sel|-->| | .----------------->| |->|Volume|->|Bias|-> L
| | |___| | | | | | |______| |____|
Ch4.L -|-|--------->| | | Ch3 | |
... -|-|--------->| | | .--------------->| |
Ch15.L-|-|--------->|_____| | | ___ | |
| '------------------o-|->|Add| Ch1+Ch3 | |
'----------------------o->|___|-------->|___|
____ _________ ___ ___ ___
|FIFO|-->|Channel 0|-->|Vol|-->|Add|-o->|Pan|--> Ch0.L
|____| |_________| |___| |___| | |___|--> Ch0.R
____ _________ ___ ^ |
|FIFO|<--|Capture 0|<--|Sel|<----|---'
|____| |_ _____ _| |___|<----|-------------- Left Mixer
____ _:Timer:_ ___ _|_ ___
|FIFO|-->|Channel 1|-->|Vol|-->|Sel|--->|Pan|--> Ch1.L
|____| |_________| |___| |___| |___|--> Ch1.R
____ _________ ___ ___
|FIFO|-->|Channel 4|-->|Vol|----------->|Pan|--> Ch4.L
|____| |_________| |___| |___|--> Ch4.R
The FIFO isn’t used in PSG/Noise modes (supported on channel 8..15).
A sound will be started/restarted when changing its start bit from 0 to 1, however, the sound won’t start immediately: PSG/Noise starts after 1 sample, PCM starts after 3 samples, and ADPCM starts after 11 samples (3 dummy samples as for PCM, plus 8 dummy samples for the ADPCM header).
In one-shot mode, the Busy bit gets cleared automatically at the BEGIN of the last sample period, nethertheless (despite of the cleared Busy bit) the last sample is kept output until the END of the last sample period (or, if the Hold flag is set, then the last sample is kept output infinitely, that is, until Hold gets cleared, or until the sound gets restarted).
The Hold flag allows to keep the last sample being output infinitely after the end of one-shot sounds. This feature is probably intended to allow to play two continous one-shot sound blocks (without producing any scratch noise upon small delays between both blocks, which would occur if the output level would drop to zero).
However, the feature doesn’t work as intended. As described above, PCM8/PCM16 sound starts are delayed by 3 samples. With Hold flag set, old output level is acually kept intact during the 1st sample, but the output level drops to zero during 2nd-3rd sample, before starting the new sound in 4th sample.
data.vol = data*N/128
pan.left = data*(128-N)/128
pan.right = data*N/128
master.vol = data*N/128/64
Register settings of 0..126,127 are interpreted as N=0..126,128.
When configured to max volume (and left-most or right-most panning), each channel can span the full 10bit output range (-200h..1FFh) on one speaker, as well as the full 16bit input range (-8000h..7FFFh) on one capture unit.
(It needs 2 channels to span the whole range on BOTH speakers/capture units.)
Together, all sixteen channels could thus reach levels up to -1E00h..21F0h (with default BIAS=200h) on one speaker, and -80000h..+7FFF0h on one capture unit. However, to avoid overflows, speaker outputs are clipped to MinMax(0,3FFh), and capture inputs to MinMax(-8000h..+7FFFh).
Step Bits Min Max
0 Incoming PCM16 Data 16.0 -8000h +7FFFh
1 Volume Divider (div 1..16) 16.4 -8000h +7FFFh
2 Volume Factor (mul N/128) 16.11 -8000h +7FFFh
3 Panning (mul N/128) 16.18 -8000h +7FFFh
4 Rounding Down (strip 10bit) 16.8 -8000h +7FFFh
5 Mixer (add channel 0..15) 20.8 -80000h +7FFF0h
6 Master Volume (mul N/128/64) 14.21 -2000h +1FF0h
7 Strip fraction 14.0 -2000h +1FF0h
8 Add Bias (0..3FFh, def=200h) 15.0 -2000h+0 +1FF0h+3FFh
9 Clip (min/max 0h..3FFh) 10.0 0 +3FFh
Table shows integer.fractional bits, and min/max values (without fraction).
Incoming ch(a) is NOT clipped, ch(a)+ch(b) may overflow (see Capture Bugs).
Incoming mixer data (20.8bits) is clipped to 16.8bits (MinMax -8000h..7FFFh).
For PCM8 capture format, the 16.8 bits are divided by 100h (=8.16 bits).
If the MSB of the fractional part is set, then data is rounded towards zero.
(Positive values are rounded down, negative values are rounded up.)
The fractional part is then discarded, and plain integer data is captured.
The output volume equals to PCM16 values +7FFFh (HIGH) and -7FFFh (LOW).
PSG sound is always Infinite (the SOUNDxLEN Register, and the SOUNDxCNT Repeat Mode bits have no effect). The PSG hardware doesn’t support sound length, sweep, or volume envelopes, however, these effects can be produced by software with little overload (or, more typically, with enormous overload, depending on the programming language used).
Each duty cycle consists of eight HIGH or LOW samples, so the sound frequency is 1/8th of the selected sample rate. The duty cycle always starts at the begin of the LOW period when the sound gets (re-)started.
0 12.5% "_______-_______-_______-"
1 25.0% "______--______--______--"
2 37.5% "_____---_____---_____---"
3 50.0% "____----____----____----"
4 62.5% "___-----___-----___-----"
5 75.0% "__------__------__------"
6 87.5% "_-------_-------_-------"
7 0.0% "________________________"
The Wave Duty bits exist and are read/write-able on all channels (although they are actually used only in PSG mode on channels 8-13).
Noise randomly switches between HIGH and LOW samples, the output levels are calculated, at the selected sample rate, as such:
X=X SHR 1, IF carry THEN Out=LOW, X=X XOR 6000h ELSE Out=HIGH
The initial value when (re-)starting the sound is X=7FFFh. The formula is more or less same as “15bit polynomial counter” used on 8bit Gameboy and GBA.
Signed samples in range -80h..+7Fh (PCM8), or -8000h..+7FFFh (PCM16).
The output volume of PCM8=NNh is equal to PCM16=NN00h.
IMA-ADPCM is a Adaptive Differential Pulse Code Modulation (ADPCM) variant, designed by International Multimedia Association (IMA), the format is used, among others, in IMA-ADPCM compressed Windows .WAV files.
The NDS data consist of a 32bit header, followed by 4bit values (so each byte contains two values, the first value in the lower 4bits, the second in upper 4 bits). The 32bit header contains initial values:
Bit0-15 Initial PCM16 Value (Pcm16bit = -7FFFh..+7FFF) (not -8000h)
Bit16-22 Initial Table Index Value (Index = 0..88)
Bit23-31 Not used (zero)
In theory, the 4bit values are decoded into PCM16 values, as such:
Diff = ((Data4bit AND 7)*2+1)*AdpcmTable[Index]/8 ;see rounding-error
IF (Data4bit AND 8)=0 THEN Pcm16bit = Max(Pcm16bit+Diff,+7FFFh)
IF (Data4bit AND 8)=8 THEN Pcm16bit = Min(Pcm16bit-Diff,-7FFFh)
Index = MinMax (Index+IndexTable[Data4bit AND 7],0,88)
In practice, the first line works like so (with rounding-error):
Diff = AdpcmTable[Index]/8
IF (data4bit AND 1) THEN Diff = Diff + AdpcmTable[Index]/4
IF (data4bit AND 2) THEN Diff = Diff + AdpcmTable[Index]/2
IF (data4bit AND 4) THEN Diff = Diff + AdpcmTable[Index]/1
And, a note on the second/third lines (with clipping-error):
Max(+7FFFh) leaves -8000h unclipped (can happen if initial PCM16 was -8000h)
Min(-7FFFh) clips -8000h to -7FFFh (possibly unlike windows .WAV files?)
Whereas, IndexTable[0..7] = -1,-1,-1,-1,2,4,6,8. And AdpcmTable [0..88] =
0007h,0008h,0009h,000Ah,000Bh,000Ch,000Dh,000Eh,0010h,0011h,0013h,0015h
0017h,0019h,001Ch,001Fh,0022h,0025h,0029h,002Dh,0032h,0037h,003Ch,0042h
0049h,0050h,0058h,0061h,006Bh,0076h,0082h,008Fh,009Dh,00ADh,00BEh,00D1h
00E6h,00FDh,0117h,0133h,0151h,0173h,0198h,01C1h,01EEh,0220h,0256h,0292h
02D4h,031Ch,036Ch,03C3h,0424h,048Eh,0502h,0583h,0610h,06ABh,0756h,0812h
08E0h,09C3h,0ABDh,0BD0h,0CFFh,0E4Ch,0FBAh,114Ch,1307h,14EEh,1706h,1954h
1BDCh,1EA5h,21B6h,2515h,28CAh,2CDFh,315Bh,364Bh,3BB9h,41B2h,4844h,4F7Eh
5771h,602Fh,69CEh,7462h,7FFFh
The closest way to reproduce the AdpcmTable with 32bit integer maths appears:
X=000776d2h, FOR I=0 TO 88, Table[I]=X SHR 16, X=X+(X/10), NEXT I
Table[3]=000Ah, Table[4]=000Bh, Table[88]=7FFFh, Table[89..127]=0000h
When using ADPCM and loops, set the loopstart position to the data part, rather than the header. At the loop end, the SAD value is reloaded to the loop start location, additionally index and pcm16 values are reloaded to the values that have originally appeared at that location. Do not change the ADPCM loop start position during playback.
For Microphone (and Touchscreen) inputs, see
Sound data is often stored in a SDAT file (with SSEQ, SSAR, SBNK, SWAR, STRM blocks inside of the SDAT files). Samples can be stored in SWAV files (or be contained in SWAR’s inside of SDAT files).
000h 4 ID "SDAT" ;alike "CSAR" on 3DS
004h 2 Byte Order (FEFFh)
006h 2 Version (0100h)
008h 4 Total Filesize
00Ch 2 Header Size (usually 40h)
00Eh 2 Number of Blocks (usually 4 = SYMB+INFO+FAT+FILE) (or 3=no SYMB)
010h 4+4 SYMB Block (Offset from SDAT+0, Size) ;=0,0 if above is 3=no SYMB
018h 4+4 INFO Block (Offset from SDAT+0, Size) ;\
020h 4+4 FAT Block (Offset from SDAT+0, Size) ; always present
028h 4+4 FILE Block (Offset from SDAT+0, Size) ;/
030h 10h Padding to 20h-byte boundary (0)
The SYMB block exists in most SDAT files (except in some titles like Downhill Jam and Over the Hedge).
_________________________________ SYMB Block _________________________________
000h 4 ID "SYMB"
004h 4 SYMB Block Size (rounded up to 4-byte boundary, unlike as in SDAT)
008h 4 File List SSEQ (Offset from SYMB+0) Sequences (songs)
00Ch 4 Folder List SSAR (Offset from SYMB+0) Sequence Archives (fx)
010h 4 File List BANK (Offset from SYMB+0) Banks
014h 4 File List SWAR (Offset from SYMB+0) Wave Archives (samples)
018h 4 File List Player (Offset from SYMB+0) Player (Group-related)
01Ch 4 File List Group (Offset from SYMB+0) Group (SSEQ+SSAR+BANK+SWAR)
020h 4 File List Player2 (Offset from SYMB+0) Player2 (Stream-related)
024h 4 File List STRM (Offset from SYMB+0) Wave Stream
028h 18h Reserved (0)
040h .. File/Folder Lists (see below)
.. .. File/Folder Name Strings (ASCII, terminated by 0)
.. .. Padding to 4-byte boundary (0)
File List’s are having following format:
000h 4 Number of entries in this list (can be 0=None)
004h N*4 File Name (Offset from SYMB+0)
Folder List (for SSAR):
000h 4 Number of entries in this list (can be 0=None)
004h N*(4+4) SSAR "Folder Name" and SSEQ "File List" (Offset's from SYMB+0)
_________________________________ INFO Block _________________________________
000h 4 ID "INFO"
004h 4 INFO Block Size (same as in SDAT header)
008h 4 Info List SSEQ (Offset from INFO+0) Sequences (songs)
00Ch 4 Info List SSAR (Offset from INFO+0) Sequence Archives (fx)
010h 4 Info List BANK (Offset from INFO+0) Banks
014h 4 Info List SWAR (Offset from INFO+0) Wave Archives (samples)
018h 4 Info List Player (Offset from INFO+0) Player (Group-related)
01Ch 4 Info List Group (Offset from INFO+0) Group (SSEQ+SSAR+BANK+SWAR)
020h 4 Info List Player2 (Offset from INFO+0) Player2 (Stream-related)
024h 4 Info List STRM (Offset from INFO+0) Wave Stream
028h 18h Reserved (0)
.. .. Info Lists (see below)
.. .. Info Entries (see below)
.. .. Padding to 4-byte boundary (0)
Info List’s are having following format:
000h 4 Number of entries in this list (can be 0=None)
004h N*4 Info Entries (Offset from INFO+0)
000h 2 FAT fileID of SSEQ file ;for accessing this file
002h 2 Unknown
004h 2 bnk ;Associated BANK
006h 1 vol ;Volume
007h 1 cpr
008h 1 ppr
009h 1 ply
00Ah 2 Unknown (0)
000h 2 FAT fileID of SSAR file
002h 2 unknown
Note: bnk/vol/cpr/ppr/ply is stored in the SSAR file (instead of in Info). The actual sequences are also in SSAR? Or does the SSAR contain pointers to SSEQ files?
000h 2 FAT fileID of SBNK file
002h 2 unknown
004h 2 1st SWAR ;\
006h 2 2nd SWAR ; Associated Wave Archives (FFFFh=Unused entry)
008h 2 3rd SWAR ;
00Ah 2 4th SWAR ;/
000h 2 FAT fileID of SWAR file
002h 2 unknown
000h 1 Unknown
001h 3 Padding
004h 4 Unknown
000h 4 Number of items in this group
004h N*(4+4) Array (with ID+Index pairs)
ID values: 700h=SSEQ, 803h=SSAR, 601h=BANK, 402h=SWAR.
Index: Entry number in the corresponding SSEQ/SSAR/BANK/SWAR list.
000h 1 nCount ;number of USED entries in below array
001h 16 v[16] ;unknown array (UNUSED entries are set to FFh
011h 7 Reserved (0)
000h 2 FAT fileID of STRM file ;for accessing the file
002h 2 Unknown
004h 1 vol ;volume
005h 1 pri ;priority?
006h 1 ply ;play?
007h 5 Reserved (0)
____________________________ FAT and FILE Blocks _____________________________
000h 4 ID "FAT "
004h 4 FAT Block Size (same as in SDAT header) (0Ch+N*10h)
008h 4 Number of files
00Ch N*(4+4+8) File Entries (Offset from SDAT+0, Size, Zero)
The 8-byte Zero entries can be used for storing data at runtime.
000h 4 ID "FILE"
004h 4 FILE Block Size (same as in SDAT header)
008h 4 Number of files (same as in FAT block)
00Ch 4 Reserved (0)
010h .. Files (SSEQ,SSAR,SBNK,SWAR,STRM) (at offsets specified in FAT)
It is a converted MIDI sequence. Linked to a BANK for instruments.
SSEQ is usually found inside of SDAT files (but also exists as standalone file, eg. 3DS Circle Pad Pro test/calib, RomFS:\extrapad_bcwav_LZ.bin\.sseq, and 3DS Picture Picker, RomFS:\sound\csnd.LZ\).
000h 4 ID "SSEQ" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 1 = DATA) ;/
010h 4 ID "DATA" ;\
014h 4 Total Filesize, minus 10h ; Sub header
018h 4 Offset to data (from SSEQ+0) (1Ch) ;/
01Ch .. Arrays of sequence data.. ;-
NB. For the details of the SSEQ file, please refer to loveemu’s sseq2mid
A SSEQ can have at maximum 16 tracks, notes in the range of 0..127 (middle C is 60). Each quartet note has a fixed tick length of 48. Tempo in the range of 1..240 BPM (default is 120). The SSEQ will not be played correctly if tempo higher than 240.
The SEQ player uses ARM7’s Timer1 for timing. The ARM7’s 4 Timers runs at 33MHz (approximately 2^25). The SEQ player sets Timer1 reload value to 2728, prescaler to F/64. So on about every 0.0052 sec (64*2728/33MHz) the SEQ Player will be notified (1 cycle). As a quartet note has fixed tick value of 48, the highest tempo that SEQ Player can handle is 240 BPM (60/(0.0052*48)).
During each cycle, the SEQ player adds the tempo value to a variable. Then it checks if the value exceeds 240. If it does, the SEQ player subtracts 240 from the variable, and process the SSEQ file. Using this method, the playback is not very precise but the difference is too small to be noticed.
Take an example with tempo = 160 BPM, the SSEQ file is processed twice in 3 notifications.
cycle variable action
1 0 Add 160
2 160 Add 160
3 320 Subtract 240, process once, add 160
4 240 Subtract 240, process once, add 160
5 160 Add 160
6 320 Subtract 240, process once, add 160
7 240 Subtract 240, process once, add 160
8 160 Add 160
ID Parameter Description
00h-7Fh Velocity: 1 byte [0..127]
Duration: Variable Length
NOTE-ON. Duration is expressed in tick.
48 for quartet note.
Usually it is NOT a multiple of 3.
80h Duration: Variable Length
REST. It tells the SSEQ-sequencer to wait for
a certain tick. Usually it is a multiple of 3.
81h Bank & Program Number:
Variable Length
bits[0..7] is the program number,
bits[8..14] is the bank number.
Bank change is seldomly found,
so usually bank 0 is used.
FEh 2 bytes Indicates which tracks are used.
Bit0 for track 0, ... Bit15 for track 15.
If the bit is set, the corresponding track is used.
Indication begin of multitrack. Must be in the
beginning of the first track to work. A series
of event 0x93 follows.
93h 4 bytes 1st byte is track number [0..15]
The other 3 bytes are the relative adress of track data.
Add nDataOffset (usually 0x1C) to find out the absolute address.
SSEQ is similar to MIDI in that track data are
stored one after one track. Unlike mod music.
94h JUMP Address: 3 bytes
(Add nDataOffset (usually 0x1C) to find out the absolute address.)
JUMP. A jump must be backward. So that the
song will loop forever.
95h CALL Address: 3 bytes
(Add nDataOffset (usually 0x1C) to find out the absolute address.)
A0h-BFh See loveemu's sseq2mid for more details.
Some arithmetic operations / comparions.
Affect how SSEQ is to be played.
C0h 1 byte PAN (0..127, middle is 64, uh?)
C1h 1 byte VOLUME (0..127)
C2h 1 byte MASTER VOLUME (0..127)
C3h 1 byte TRANSPOSE (Channel Coarse Tuning) (0..64 = 64..128 in MIDI)
C4h 1 byte PITCH BEND
C5h 1 byte PITCH BEND RANGE
C6h 1 byte TRACK PRIORITY
C7h 1 byte MONO/POLY (0=Poly, 1=Mono)
C8h 1 byte TIE (unknown) (0=Off, 1=On)
C9h 1 byte PORTAMENTO CONTROL
CAh 1 byte MODULATION DEPTH (0=Off, 1=On)
CBh 1 byte MODULATION SPEED
CCh 1 byte MODULATION TYPE (0=Pitch, 1=Volume, 2=Pan)
CDh 1 byte MODULATION RANGE
CEh 1 byte PORTAMENTO ON/OFF
CFh 1 byte PORTAMENTO TIME
D0h 1 byte ATTACK RATE
D1h 1 byte DECAY RATE
D2h 1 byte SUSTAIN RATE
D3h 1 byte RELEASE RATE
D4h 1 byte LOOP START (how many times to be looped)
D5h 1 byte EXPRESSION
D6h 1 byte PRINT VARIABLE (unknown)
E0h 2 byte MODULATION DELAY
E1h 2 byte TEMPO
E3h 2 byte SWEEP PITCH
FCh - LOOP END (for LOOP START)
FDh - RETURN from CALL command
FFh - EOT: End Of Track
It is a collection of SSEQ sequences (mainly for relative short sound effects) (longer sequences like music are usually stored in separate SSEQ files instead of in SSAR archives).
000h 4 ID "SSAR" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 1 = DATA) ;/
010h 4 ID "DATA" ;\
014h 4 Total Filesize, minus 10h ;
018h 4 Offset to data (from SSAR+0) (20h+N*0Ch) ; Sub header
01Ch 4 Number of records ;
020h N*0Ch Records (12 bytes each) ;/
.. .. data... unknown content? alike SSEQ? ;-
000h 4 nOffset ;relative offset of the archived SEQ file,
absolute offset = nOffset + SSAR::nDataOffset
004h 2 bnk ;bank
006h 1 vol ;volume
007h 1 cpr ;channel pressure
008h 1 ppr ;polyphonic pressure
009h 1 ply ;play
00Ah 2 reserved (0)
data... unknown content? alike SSEQ?
NB. Archived SSEQ files are not stored in sequence (order). So Rec[0].nOffset may point to 0x100 but Rec[1].nOffset points to 0x40.
NB. Archived SSEQ files cannot be readily extracted from SSAR file because data in one SSEQ may ‘call’ data in other SSEQ.
This seems to assign ADSR patterns to each note of each SWAV instrument…?
“A bank is linked to up to 4 SWAR files which contain the samples. It defines the instruments which a SSEQ sequence can use. You may imagine SSEQ + SBNK + SWAR are similar to module music created by trackers.” uh?
000h 4 ID "SBNK" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 1 = DATA) ;/
010h 4 ID "DATA" ;\
014h 4 Total Filesize, minus 10h ;
018h 20h Reserved (0) (for use at runtime) ; Sub header
038h 4 Number of Instruments (SWAV's) ;
03Ch N*4 Instrument Records (1+2+1 bytes per instr.) ;/
... .. Instrument Data (depending of above records) ;-
000h 1 fRecord ;can be either 0, 1..4, 16 or 17
001h 2 nOffset ;absolute offset of the data in file ;uh, misaligned?
003h 1 Reserved (0)
nOffset will also = 0.
“I have seen values 1, 2 and 3. But it seems the value does not affect the wave/note definition that follows. Instrument record size is 10 bytes.”
00h 10 SWAV, SWAR, Note, Attack, Decay, Sustain, Release, Pan
00h 1 Lower note (0..127) ;eg. 10 ;\notes 10..20
01h 1 Upper note (0..127) ;eg. 20 ;/
02h+N*12 2 Unknown (usually 0001h)
04h+N*12 10 SWAV, SWAR, Note, Attack, Decay, Sustain, Release, Pan
00h 1 End of 1st region (0..127) ;eg. 25 = notes 0..25
01h 1 End of 2nd region (0..127) ;eg. 35 = notes 26..35
02h 1 End of 3rd region (0..127) ;eg. 45 = notes 36..45
03h 1 End of 4th region (0..127) ;eg. 55 = notes 46..55
04h 1 End of 5th region (0..127) ;eg. 65 = notes 56..65
05h 1 End of 6th region (0..127) ;eg. 127 = notes 66..last
06h 1 End of 7th region (0..127) ;eg. 0 = none
07h 1 End of 8th region (0..127) ;eg. 0 = none
08h+N*12 2 Unknown (usually 0001h)
08h+N*12 10 SWAV, SWAR, Note, Attack, Decay, Sustain, Release, Pan
REMARKS: Unknown bytes before wave/defnition definition = 5, not 1 in stage_04_bank.sbnk, stage_04.sdat, Rom No.1156, uh?
00h 2 SWAV Number the swav used
02h 2 SWAR Mumber the swar used (see Info Block --> "BANK Info Entry")
04h 1 Note Number (0..127)
05h 1 Attack Rate (0..127, 127=fast)
06h 1 Decay Rate (0..127, 127=fast)
07h 1 Sustain Level (0..127, 127=stay at max, no decay)
08h 1 Release Rate (0..127, 127=fast)
09h 1 Pan (0..127, 64=middle) (uh, what=left, what=right?)
. <-- max level (127)
/ \
/ \
/ '---------. <-- sustain level (0..127)
/ \
/ \
-----'---------------------'-- <-- min level (0)
Attack Decay Sustain Release
"The SEQ Player treats 0 as the 100% amplitude value and -92544 (723*128)
as the 0% amplitude value. The starting ampltitude is 0% (-92544)." uh?
"During the attack phase, in each cycle, the SSEQ Player calculates the new
amplitude value: amplitude value = attack rate * amplitude value / 255. The
attack phase stops when amplitude reaches 0." THAT IS... NON-LINEAR attack?
"During the decay phase, in each cycle, the SSEQ Player calculates the new
amplitude value: amplitude value = amplitude value - decay rate.
Note the starting amplitude value is 0. The decay phase stops when
amplitude reaches sustain level." THAT IS... LINEAR decay/release?
It is a collection of mono wave (SWAV) samples only (which can be in either PCM8, PCM16 or ADPCM compression).
000h 4 ID "SWAR" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize (including SWAV's) ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 1 = DATA) ;/
010h 4 ID "DATA" ;\
014h 4 Total Filesize, minus 10h ;
018h 20h Reserved (0) (for use at runtime) ; Sub header
038h 4 Number of SWAV sample blocks ;
03Ch N*4 Offsets to Sample blocks (from SWAR+0) ;/
.. .. Sample blocks... starting with Type (0=PCM8, 1=PCM16, 2=IMA-ADPCM)
When extracting single sample block, one can convert them to SWAV files (by inserting an 18h-byte SWAV header).
The sample blocks are usually (always?) stored at increasing offsets (so one can determine the size by computing the distance to next offset; or to filesize for last entry) (alternately, the size can be computed by looking at the Sound Length entry of sample block).
000h 4 ID "SWAV" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 1 = DATA) ;/
010h 4 ID "DATA" ;\Sub header
014h 4 Total Filesize, minus 10h ;/
018h .. Sample block (see below)
Note: System Flaw has a lot of SWAV files (instead of using SWAR archives).
000h 1 WaveType (0=PCM8, 1=PCM16, 2=IMA-ADPCM)
001h 1 Loop flag = TRUE|FALSE ;uh?
002h 2 Sampling Rate
004h 2 Time (ARM7_CLOCK / nSampleRate)
[ARM7_CLOCK: 33.513982MHz/2 = 1.6756991 E +7]
006h 2 Loop Offset, in 4-byte units
008h 4 Sound Length, in 4-byte units (exluding ADPCM header, if any)
00Ch ... Data... (samples) (with 32bit header in case of ADPCM)
It is an individual mono/stereo wave file (PCM8, PCM16 or ADPCM) (eg. used in Ultimate Spiderman rom:\sound\sound_stream.sdat).
000h 4 ID "STRM" ;\
004h 2 Byte Order (FEFFh) ;
006h 2 Version (0100h) ; Main header
008h 4 Total Filesize ;
00Ch 2 Header Size (usually 10h) ;
00Eh 2 Number of Blocks (usually 2 = HEAD+DATA) ;/
010h 4 ID "HEAD" ;\
014h 4 Size of HEAD structure (uh, this is... 50h?) ;
018h 1 Type (0=PCM8, 1=PCM16, 2=IMA-ADPCM) ; Sub header
019h 1 Loop flag (?=TRUE|FALSE) ;uh? ;
01Ah 1 Channels (?=What) ;mono/stereo? ;
01Bh 1 Unknown (always 0) ;
01Ch 2 Sampling Rate (perhaps resampled from original) ;
01Eh 2 Time (1.0 / rate * ARM7_CLOCK / 32) ;
[ARM7_CLOCK: 33.513982MHz/2 = 1.6756991e7] ;
020h 4 Loop Offset (samples) ;
024h 4 Number of Samples ;
028h 4 Wave Data Offset (always 68h) ;
02Ch 4 Number of Blocks (per what?) ;
030h 4 Block Length (per Channel) ;
034h 4 Samples Per Block (per Channel) ;
038h 4 Last Block Length (per Channel) ;
03Ch 4 Samples Per Last Block (per Channel) ;
040h 20h Reserved (always 0) ;/
060h 4 ID "DATA" ;\Data header
064h 4 Data Size (8+N ?) ;/
068h N Wave Data blocks... ;-Sample data
Mono blocks are ordered: Block1, Block2, Block3, etc.
Stereo blocks are ordered: LeftBlock1, RightBlock1, LeftBlock2, etc.
The DS includes four DMA channels for each CPU (ie. eight channels in total), which are working more or less the same as on GBA:
Word count of all channels is expanded to 21bits (max 1..1FFFFFh units, or 0=200000h units), and SAD/DAD registers for all channels support ranges of 0..0FFFFFFEh. The transfer modes (DMACNT Bit27-29) are:
0 Start Immediately
1 Start at V-Blank
2 Start at H-Blank (paused during V-Blank)
3 Synchronize to start of display
4 Main memory display
5 DS Cartridge Slot
6 GBA Cartridge Slot
7 Geometry Command FIFO
Word Count, SAD, and DAD are R/W, aside from that they do have the same restrictions as on GBA (max 4000h or 10000h units, some addresses limited to 0..07FFFFFEh). DMACNT Bit27 is unused on NDS7. The transfer modes (DMACNT Bit28-29) are:
0 Start Immediately
1 Start at V-Blank
2 DS Cartridge Slot
3 DMA0/DMA2: Wireless interrupt, DMA1/DMA3: GBA Cartridge Slot
Bit0-31 Filldata
The DMA Filldata registers contain 16 bytes of general purpose WRAM, intended to be used as fixed source addresses for DMA memfill operations.
This is useful because DMA cannot read from TCM, and reading from Main RAM would require to recurse cache & write buffer.
The DMA Filldata is used with Src=Fixed and SAD=40000Exh (which isn’t optimal because it’s doing repeated reads from SAD, and, for that reason, a memfill via STMIA opcodes can be faster than DMA; the DSi’s new NDMA channels are providing a faster fill method with Src=Fill and SAD=Unused).
The NDS additionally includes 16 Sound DMA channels, plus 2 Sound Capture DMA channels (see Sound chapter). The priority of these channels is unknown.
Cache and tightly coupled memory are connected directly to the NDS9 CPU, without using the system bus. So that, DMA cannot access DTCM/ITCM, and access to cached memory regions must be handled with care: Drain the writebuffer before DMA-reads, and invalidate the cache after DMA-writes. See,
Respectively, interrupts executed during DMA will usually halt the CPU (unless the IRQ handler uses only TCM and cache; the IRQ vector at FFFF00xxh must be cached, or relocated to ITCM at 000000xxh, and the IRQ handler may not access IE, IF, or other I/O ports).
Main RAM has different access time for sequential and non-sequential access. Normally DMA uses sequential access (except for the first word), however, if the source and destination addresses are both in Main RAM, then all accesses become non-sequential. In that case it would be faster to use two DMA transfers, one from Main RAM to a scratch buffer in WRAM, and one from WRAM to Main RAM.
Same as GBA, except F = 33.513982 MHz (for both NDS9 and NDS7).
The NDS sound controller is having its own frequency generators (unlike GBA, which needed to use Timers to drive channel A/B sounds).
0 Disable all interrupts (0=Disable All, 1=See IE register)
1-31 Not used
Bits in the IE register are 0=Disable, 1=Enable.
Reading IF returns 0=No request, 1=Interrupt Request.
Writing IF acts as 0=No change, 1=Acknowledge (clears that bit).
0 LCD V-Blank
1 LCD H-Blank
2 LCD V-Counter Match
3 Timer 0 Overflow
4 Timer 1 Overflow
5 Timer 2 Overflow
6 Timer 3 Overflow
7 NDS7 only: SIO/RCNT/RTC (Real Time Clock)
8 DMA 0
9 DMA 1
10 DMA 2
11 DMA 3
12 Keypad
13 GBA-Slot (external IRQ source) / DSi: None such
14 Not used / DSi9: NDS-Slot Card change?
15 Not used / DSi: dito for 2nd NDS-Slot?
16 IPC Sync
17 IPC Send FIFO Empty
18 IPC Recv FIFO Not Empty
19 NDS-Slot Game Card Data Transfer Completion
20 NDS-Slot Game Card IREQ_MC
21 NDS9 only: Geometry Command FIFO
22 NDS7 only: Screens unfolding
23 NDS7 only: SPI bus
24 NDS7 only: Wifi / DSi9: XpertTeak DSP
25 Not used / DSi9: Camera
26 Not used / DSi9: Undoc, IF.26 set on FFh-filling 40021Axh
27 Not used / DSi: Maybe IREQ_MC for 2nd gamecard?
28 Not used / DSi: NewDMA0
29 Not used / DSi: NewDMA1
30 Not used / DSi: NewDMA2
31 Not used / DSi: NewDMA3
? DSi7: any further new IRQs on ARM7 side... in bit13-15,21,25-26?
Raw TCM-only IRQs can be processed even during DMA ?
Trying to set all IE bits gives FFFFFFFFh (DSi7) or FFFFFF7Fh (DSi9).
0 DSi7: GPIO18[0] ;\
1 DSi7: GPIO18[1] ; maybe 1.8V signals?
2 DSi7: GPIO18[2] ;/
3 DSi7: Unused (0)
4 DSi7: GPIO33[0] unknown (related to "GPIO330" testpoint on mainboard?)
5 DSi7: GPIO33[1] Headphone connect (HP#SP) (static state)
6 DSi7: GPIO33[2] Powerbutton interrupt (short pulse upon key-down)
7 DSi7: GPIO33[3] sound enable output (ie. not a useful irq-input)
8 DSi7: SD/MMC Controller ;-Onboard eMMC and External SD Slot
9 DSi7: SD Slot Data1 pin ;-For SDIO hardware in External SD Slot
10 DSi7: SDIO Controller ;\Atheros Wifi Unit
11 DSi7: SDIO Data1 pin ;/
12 DSi7: AES interrupt
13 DSi7: I2C interrupt
14 DSi7: Microphone Extended interrupt
15-31 DSi7: Unused (0)
Trying to set all IE2 bits gives 00007FF7h (DSi7) or 00000000h (DSi9).
Bit 0-31 Pointer to IRQ Handler
NDS7 Handler must use ARM code, NDS9 Handler can be ARM/THUMB (Bit0=Thumb).
Bit 0-31 IRQ Flags (same format as IE/IF registers)
When processing & acknowleding interrupts via IF register, the user interrupt handler should also set the corresponding bits of the IRQ Check value (required for BIOS IntrWait and VBlankIntrWait SWI functions).
Same as the above 380FFF8h value, but for new IE2/IF2 registers, intended for use with IntrWait and VBlankIntrWait functions. However, that functions are BUGGED on DSi and won’t actually work in practice (they do support only the new 380FFC0h bits, but do accidently ignore the old 380FFF8h bits).
— Below for other (non-IRQ) exceptions —
These addresses contain a 32bit pointer to the Debug Handler, and, memory below of the addresses is used as Debug Stack. The debug handler is called on undefined instruction exceptions, on data/prefetch aborts (caused by the protection unit), on FIQ (possibly caused by hardware debuggers). It is also called by accidental software-jumps to the reset vector, and by unused SWI numbers within range 0..1Fh.
0-1 Division Mode (0-2=See below) (3=Reserved; same as Mode 1)
2-13 Not used
14 Division by zero (0=Okay, 1=Division by zero error; 64bit Denom=0)
15 Busy (0=Ready, 1=Busy) (Execution time see below)
16-31 Not used
Division Modes and Busy Execution Times
Mode Numer / Denom = Result, Remainder ; Cycles
0 32bit / 32bit = 32bit , 32bit ; 18 clks
1 64bit / 32bit = 64bit , 32bit ; 34 clks
2 64bit / 64bit = 64bit , 64bit ; 34 clks
Division is started when writing to any of the DIVCNT/NUMER/DENOM registers.
Signed 64bit values (or signed 32bit values in 32bit modes, the upper 32bits are then unused, with one exception: the DIV0 flag in DIVCNT is set only if the full 64bit DIV_DENOM value is zero, even in 32bit mode).
Signed 64bit values (in 32bit modes, the values are sign-expanded to 64bit).
Overflows occur on “DIV0” and “-MAX/-1” (eg. -80000000h/-1 in 32bit mode):
DIV0 --> REMAIN=NUMER, RESULT=+/-1 (with sign opposite of NUMER)
-MAX/-1 --> RESULT=-MAX (instead +MAX)
On overflows in 32bit/32bit=32bit mode: the upper 32bit of the sign-expanded 32bit result are inverted. This feature produces a correct 64bit (+MAX) result in case of the incorrect 32bit (-MAX) result. The feature also applies on DIV0 errors (which makes the sign-expanded 64bit result even more messed-up than the normal 32bit result).
The DIV0 flag in DIVCNT.14 indicates DENOM=0 errors (it does not indicate “-MAX/-1” errors). The DENOM=0 check relies on the full 64bit value (so, in 32bit mode, the flag works only if the unused upper 32bit of DENOM are zero).
0 Mode (0=32bit input, 1=64bit input)
1-14 Not used
15 Busy (0=Ready, 1=Busy) (Execution time is 13 clks, in either Mode)
16-31 Not used
Calculation is started when writing to any of the SQRTCNT/PARAM registers.
Unsigned 64bit parameter, and unsigned 32bit result.
Push all DIV/SQRT values (parameters and control registers) when using DIV/SQRT registers on interrupt level, and, after restoring them, be sure to wait until the busy flag goes off, before leaving the IRQ handler.
The NDS9 and NDS7 BIOSes additionally contain software based division and square root functions, which are NOT using above hardware registers (even the NDS9 functions are raw software).
The Div/Sqrt timings are counted in 33.51MHz units. Although the calculations are quite fast, mind that reading/writing the result/parameter registers takes up additional clock cycles (especially due to the PENALTY cycle glitch for non-sequential accesses; parts of that problem can be eventually bypassed by using sequential STMIA/LDMIA opcodes) (nethertheless, in some cases, software may be actually faster than the hardware registers; eg. for small 8bit numbers; that of course NOT by using the BIOS software functions which are endless inefficient).
Allows to exchange status information between ARM7 and ARM9 CPUs.
The register can be accessed simultaneously by both CPUs (without violating access permissions, and without generating waitstates at either side).
Bit Dir Expl.
0-3 R Data input from IPCSYNC Bit8-11 of remote CPU (00h..0Fh)
4-7 - Not used
8-11 R/W Data output to IPCSYNC Bit0-3 of remote CPU (00h..0Fh)
12 - Not used
13 W Send IRQ to remote CPU (0=None, 1=Send IRQ)
14 R/W Enable IRQ from remote CPU (0=Disable, 1=Enable)
15-31 - Not used
Bit Dir Expl.
0 R Send Fifo Empty Status (0=Not Empty, 1=Empty)
1 R Send Fifo Full Status (0=Not Full, 1=Full)
2 R/W Send Fifo Empty IRQ (0=Disable, 1=Enable)
3 W Send Fifo Clear (0=Nothing, 1=Flush Send Fifo)
4-7 - Not used
8 R Receive Fifo Empty (0=Not Empty, 1=Empty)
9 R Receive Fifo Full (0=Not Full, 1=Full)
10 R/W Receive Fifo Not Empty IRQ (0=Disable, 1=Enable)
11-13 - Not used
14 R/W Error, Read Empty/Send Full (0=No Error, 1=Error/Acknowledge)
15 R/W Enable Send/Receive Fifo (0=Disable, 1=Enable)
16-31 - Not used
Bit0-31 Send Fifo Data (max 16 words; 64bytes)
Bit0-31 Receive Fifo Data (max 16 words; 64bytes)
When IPCFIFOCNT.15 is disabled: Writes to IPCFIFOSEND are ignored (no data is stored in the FIFO, the error bit doesn’t get set though), and reads from IPCFIFORECV return the oldest FIFO word (as usually) (but without removing the word from the FIFO).
When the Receive FIFO is empty: Reading from IPCFIFORECV returns the most recently received word (if any), or ZERO (if there was no data, or if the FIFO was cleared via IPCFIFOCNT.3), and, in either case the error bit gets set.
The Fifo-IRQs are edge triggered, IF.17 gets set when the condition “(IPCFIFOCNT.2 AND IPCFIFOCNT.0)” changes from 0-to-1, and IF.18 gets set when “(IPCFIFOCNT.10 AND NOT IPCFIFOCNT.8)” changes from 0-to-1. The IRQ flags can be acknowledged even while that conditions are true.
For the GBA-buttons: Same as GBA, both ARM7 and ARM9 have keyboard input registers, and each its own keypad IRQ control register.
For Touchscreen (and Microphone) inputs, see
0 Button X (0=Pressed, 1=Released)
1 Button Y (0=Pressed, 1=Released)
3 DEBUG button (0=Pressed, 1=Released/None such)
6 Pen down (0=Pressed, 1=Released/Disabled) (always 0 in DSi mode)
7 Hinge/folded (0=Open, 1=Closed)
2,4,5 Unknown / set
8..15 Unknown / zero
The Hinge stuff is a magnetic sensor somewhere underneath of the Start/Select buttons (NDS) or between A/B/X/Y buttons (DSi), it will be triggered by the magnet field from the right speaker when the console is closed. The hinge generates an interrupt request (there seems to be no way to disable this, unlike as for all other IRQ sources), however, the interrupt execution can be disabled in IE register (as for other IRQ sources).
The Pen Down is the /PENIRQ signal from the Touch Screen Controller (TSC), if it is enabled in the TSC control register, then it will notify the program when the screen pressed, the program should then read data from the TSC (if there’s no /PENIRQ then doing unneccassary TSC reads would just waste CPU power). However, the user may release the screen before the program performs the TSC read, so treat the screen as not pressed if you get invalid TSC values (even if /PENIRQ was LOW).
Not sure if the TSC /PENIRQ is actually triggering an IRQ in the NDS?
The Debug Button should be connected to R03 and GND (on original NDS, R03 is the large soldering point between the SL1 jumper and the VR1 potentiometer) (there is no R03 signal visible on the NDS-Lite board).
Interrupts are reportedly not supported for X,Y buttons.
The DS doesn’t have a Serial Link Port Socket, however, internally, the NDS7 contains the complete set of Serial I/O Ports, as contained in the GBA:
In GBA mode, the ports are working as on real GBA (as when no cable is connected). In NDS mode, the ports are even containing some additional bits:
NDS7 4000128h SIOCNT Bit15 "CKUP" New Bit in NORMAL/MULTI/UART mode (R/W)
NDS7 4000128h SIOCNT Bit14 "N/A" Removed IRQ Bit in UART mode (?)
NDS7 400012Ah SIOCNT_H Bit14 "TFEMP" New Bit (R/W)
NDS7 400012Ah SIOCNT_H Bit15 "RFFUL" New Bit (always zero?)
NDS7 400012Ch SIOSEL Bit0 "SEL" New Bit (always zero?)
NDS7 4000140h JOYCNT Bit7 "MOD" New Bit (R/W)
The “CKUP” bit duplicates the internal clock transfer rate (selected in SIOCNT.1) (tested in normal mode) (probably works also in multi/uart mode?).
DS-Lite Firmware writes FFFFh to this address (prior to accessing SIOCNT), so it’s probably SIO or debugging related (might be as well a bug or so). Reading from the port always returns 0000h on both DS and DS-Lite.
NDS9 4000120h SIODATA32 Bit0-31 Data (always zero?)
NDS9 4000128h SIOCNT Bit2 "TRECV" New Bit (always zero?)
NDS9 4000128h SIOCNT Bit3 "TSEND" New Bit (always zero?)
NDS9 400012Ch SIOSEL Bit0 "SEL" New Bit (always zero?)
Not sure if these ports really exist in the release-version, or if it’s been prototype stuff?
RCNT (4000134h) should be set to 80xxh (general purpose mode) before accessing EXTKEYIN (4000136h) or RTC (4000138h). No idea why (except when using RTC/SI-interrupt).
The SI line is labeled “INT” on the NDS mainboard, it is connected to Pin 1 of the RTC chip (ie. the /INT interrupt pin).
I have no idea where to find SO, SC, and SD. I’ve written a test proggy that pulsed all four RCNT bits - but all I could find was the SI signal. However, the BIOS contains some code that uses SIO normal mode transfers (for the debug version), so at least SI, SO, SC should exist…?
MAYBE that three signals are somehow replaced by EXTKEYIN bit0,1,3?
Seiko Instruments Inc. S-35180 (compatible with S-35190A)
Miniature 8pin RTC with 3-wire serial bus
Seiko S-35199A01 (12pin BGA, with some extra functions like FOUT and Alarm Date)
Bit Expl.
0 Data I/O (0=Low, 1=High)
1 Clock Out (0=Low, 1=High)
2 Select Out (0=Low, 1=High/Select)
4 Data Direction (0=Read, 1=Write)
5 Clock Direction (should be 1=Write)
6 Select Direction (should be 1=Write)
3,8-11 Unused I/O Lines
7,12-15 Direction for Bit3,8-11 (usually 0)
16-31 Not used
Chipselect and Command/Parameter Sequence:
Init CS=LOW and /SCK=HIGH, and wait at least 1us
Switch CS=HIGH, and wait at least 1us
Send the Command byte (see bit-transfer below)
Send/receive Parameter byte(s) associated with the command (see below)
Switch CS to LOW
Bit transfer (repeat 8 times per cmd/param byte) (bits transferred LSB first):
Output /SCK=LOW and SIO=databit (when writing), then wait at least 5us
Output /SCK=HIGH, wait at least 5us, then read SIO=databit (when reading)
In either direction, data is output on (or immediately after) falling edge.
Ideally, <both> commands and parameters should be transmitted LSB-first (unlike the original Seiko document, which recommends LSB-first for data, and MSB-first for commands) (actually, later Seiko datasheets are going so far to recommend MSB-first for everything, eg. to use bit-reversed Data=C8h for Year=13h).
Command Register
Fwd Rev
0 7 Fixed Code (must be 0)
1 6 Fixed Code (must be 1)
2 5 Fixed Code (must be 1)
3 4 Fixed Code (must be 0, or, DSi only: 1=Extended Command)
4-6 3-1 Command
Fwd Rev Parameter bytes (read/write access)
0 0 1 byte, status register 1
4 1 1 byte, status register 2
2 2 7 bytes, date & time (year,month,day,day_of_week,hh,mm,ss)
6 3 3 bytes, time (hh,mm,ss)
1* 4* 1 byte, int1, frequency duty setting
1* 4* 3 bytes, int1, alarm time 1 (day_of_week, hour, minute)
5 5 3 bytes, int2, alarm time 2 (day_of_week, hour, minute)
3 6 1 byte, clock adjustment register
7 7 1 byte, free register
Extended command (when above "fourth bit" was set, DSi only)
Fwd Rev Parameter bytes (read/write access)
0 0 3 byte, up counter (msw,mid,lsw) (read only)
4 1 1 byte, FOUT register setting 1
2 2 1 byte, FOUT register setting 2
6 3 reserved
1 4 3 bytes, alarm date 1 (year,month,day)
5 5 3 bytes, alarm date 2 (year,month,day)
3 6 reserved
7 7 reserved
7 0 Parameter Read/Write Access (0=Write, 1=Read)
* INT1: Type and number of parameters depend on INT1 setting in stat reg2.
The “Fwd” bit numbers and command values for LSB-first command transfers (ie. both commands and parameters use the same bit-order).
The “Rev” numbers/values are for MSB-first command transfers (ie. commands using opposite bit-order than parameters, as being suggested by Seiko).
Status Register 1
0 W Reset (0=Normal, 1=Reset)
1 R/W 12/24 hour mode (0=12 hour, 1=24 hour)
2-3 R/W General purpose bits
4 R Interrupt 1 Flag (1=Yes) ;auto-cleared on read
5 R Interrupt 2 Flag (1=Yes) ;auto-cleared on read
6 R Power Low Flag (0=Normal, 1=Power is/was low) ;auto-cleared on read
7 R Power Off Flag (0=Normal, 1=Power was off) ;auto-cleared on read
Power off indicates that the battery was removed or fully discharged,
all registers are reset to 00h (or 01h), and must be re-initialized.
Status Register 2
0-3 R/W INT1 Mode/Enable
0000b Disable
0x01b Selected Frequency steady interrupt
0x10b Per-minute edge interrupt
0011b Per-minute steady interrupt 1 (duty 30.0 seconds)
0100b Alarm 1 interrupt
0111b Per-minute steady interrupt 2 (duty 0.0079 seconds)
1xxxb 32kHz output
4-5 R/W General purpose bits
6 R/W INT2 Enable
0b Disable
1b Alarm 2 interrupt
7 R/W Test Mode (0=Normal, 1=Test, don't use) (cleared on Reset)
Clock Adjustment Register (to compensate oscillator inaccuracy)
0-7 R/W Adjustment (00h=Normal, no adjustment)
Free Register
0-7 R/W General purpose bits
Year Register
0-7 R/W Year (BCD 00h..99h = 2000..2099)
Month Register
0-4 R/W Month (BCD 01h..12h = January..December)
5-7 - Not used (always zero)
Day Register
0-5 R/W Day (BCD 01h..28h,29h,30h,31h, range depending on month/year)
6-7 - Not used (always zero)
Day of Week Register (septenary counter)
0-2 R/W Day of Week (00h..06h, custom assignment, usually 0=Monday?)
3-7 - Not used (always zero)
Hour Register
0-5 R/W Hour (BCD 00h..23h in 24h mode, or 00h..11h in 12h mode)
6 * AM/PM (0=AM before noon, 1=PM after noon)
* 24h mode: AM/PM flag is read only (PM=1 if hour = 12h..23h)
* 12h mode: AM/PM flag is read/write-able
* 12h mode: Observe that 12 o'clock is defined as 00h (not 12h)
7 - Not used (always zero)
Minute Register
0-6 R/W Minute (BCD 00h..59h)
7 - Not used (always zero)
Second Register
0-6 R/W Minute (BCD 00h..59h)
7 - Not used (always zero)
Alarm1 and Alarm2 Day of Week Registers (INT1 and INT2 each)
0-2 R/W Day of Week (00h..06h)
3-6 - Not used (always zero)
7 R/W Compare Enable (0=Alarm every day, 1=Alarm only at specified day)
Alarm1 and Alarm2 Hour Registers (INT1 and INT2 each)
0-5 R/W Hour (BCD 00h..23h in 24h mode, or 00h..11h in 12h mode)
6 R/W AM/PM (0=AM, 1=PM) (must be correct even in 24h mode?)
7 R/W Compare Enable (0=Alarm every hour, 1=Alarm only at specified hour)
Alarm1 and Alarm2 Minute Registers (INT1 and INT2 each)
0-6 R/W Minute (BCD 00h..59h)
7 R/W Compare Enable (0=Alarm every min, 1=Alarm only at specified min)
Selected Frequency Steady Interrupt Register (INT1 only) (when Stat2/Bit2=0)
0 R/W Enable 1Hz Frequency (0=Disable, 1=Enable)
1 R/W Enable 2Hz Frequency (0=Disable, 1=Enable)
2 R/W Enable 4Hz Frequency (0=Disable, 1=Enable)
3 R/W Enable 8Hz Frequency (0=Disable, 1=Enable)
4 R/W Enable 16Hz Frequency (0=Disable, 1=Enable)
The signals are ANDed when two or more frequencies are enabled,
ie. the /INT signal gets LOW when either of the signals is LOW.
5-7 R/W General purpose bits
Note: There is only one register shared as “Selected Frequency Steady Interrupt” (accessed as single byte parameter when Stat2/Bit2=0) and as “Alarm1 Minute” (accessed as 3rd byte of 3-byte parameter when Stat2/Bit2=1), changing either value will also change the other value.
Up Counter Msw
0-7 R Up Counter bit16-23 (non-BCD, 00h..FFh)
Up Counter Mid
0-7 R Up Counter bit8-15 (non-BCD, 00h..FFh)
Up Counter Lsw
0-7 R Up Counter bit0-7 (non-BCD, 00h..FFh)
The 24bit Up Counter is incremented when seconds=00h (that is, once per minute; unless the Time is getting getting changed by write commands, which may cause some stuttering). The Up Counter starts at 000000h upon power-up, and, if the battery lasts that long: wraps from FFFFFFh to 000000h after about 30 years.
Alarm 1 and Alarm 2 Year Register
0-7 R/W Year (BCD 00h..99h = 2000..2099)
Alarm 1 and Alarm 2 Month Register
0-4 R/W Month (BCD 01h..12h = January..December)
5 - Not used (always zero)
6 R/W Year Compare Enable (0=Ignore, 1=Enable)
7 R/W Month Compare Enable (0=Ignore, 1=Enable)
Alarm 1 and Alarm 2 Day Register
0-5 R/W Day (BCD 01h..28h,29h,30h,31h, range depending on month/year)
6 - Not used (always zero)
7 R/W Day Compare Enable (0=Ignore, 1=Enable)
XXX unspecified if above Alarm Date stuff is really R/W (or write only)
FOUT Register Setting 1
0-7 R/W Enable bits (bit0=256Hz, bit1=512Hz, ..., bit7=32768Hz)
FOUT Register Setting 2
0-7 R/W Enable bits (bit0=1Hz, bit1=2Hz, ..., bit7=128Hz)
The above sixteen FOUT signals are ANDed when two or more frequencies are
enabled, ie. the FOUT signal gets LOW when either of the signals is LOW.
Note: The FOUT pin goes to the DSi’s wifi daughterboard (FOUT is configured by firmware (needed if it was changed, or when the battery was removed), FOUT is required for exchanging Atheros WMI commands/events).
There’s only one /INT signal, shared for both INT1 and INT2.
In the NDS, it is connected to the SI-input of the SIO unit (and so, also shared with SIO interrupts). To enable the interrupt, RCNT should be set to 8144h (Bit14-15=General Purpose mode, Bit8=SI Interrupt Enable, Bit6,2=SI Output/High).
The Output/High settings seems to be used as pullup (giving faster reactions on low-to-high transitions) (nethertheless, in most cases it seems to be also working okay as Input, ie. with RCNT=8100h).
The RCNT interrupt is generated on high-to-low transitions on the SI line (but only if the IRQ is enabled in RCNT.8, and only if RCNT is set to general purpose mode) (note: changing RCNT.8 from off-to-on does NOT generate IRQs, even when SI is LOW).
1 /INT 8 VDD
2 XOUT 7 SIO
3 XIN 6 /SCK
4 GND 5 CS
SPI Bus is a 4-wire (Data In, Data Out, Clock, and Chipselect) serial bus.
The NDS supports the following SPI devices (each with its own chipselect).
0-1 Baudrate (0=4MHz/Firmware, 1=2MHz/Touchscr, 2=1MHz/Powerman., 3=512KHz)
2 DSi: Baudrate MSB (4=8MHz, 5..7=None/0Hz) (when SCFG_EXT7.bit9=1)
2 NDS: Not used (Zero)
3-6 Not used (Zero)
7 Busy Flag (0=Ready, 1=Busy) (presumably Read-only)
8-9 Device Select (0=Powerman., 1=Firmware, 2=Touchscr, 3=Reserved)
10 Transfer Size (0=8bit/Normal, 1=16bit/Bugged)
11 Chipselect Hold (0=Deselect after transfer, 1=Keep selected)
12-13 Not used (Zero)
14 Interrupt Request (0=Disable, 1=Enable)
15 SPI Bus Enable (0=Disable, 1=Enable)
The “Hold” flag should be cleared BEFORE transferring the LAST data unit, the chipselect will be then automatically cleared after the transfer, the program should issue a WaitByLoop(3) manually AFTER the LAST transfer.
The SPI transfer is started on writing to this register, so one must <write> a dummy value (should be zero) even when intending to <read> from SPI bus.
0-7 Data
8-15 Not used (always zero, even in bugged-16bit mode)
During transfer, the Busy flag in SPICNT is set, and the written SPIDATA value is transferred to the device (via output line), simultaneously data is received (via input line). Upon transfer completion, the Busy flag goes off (with optional IRQ), and the received value can be then read from SPIDATA, if desired.
SPICNT Bits 12,13 appear to be unused (always zero), although the BIOS (attempts to) set Bit13=1, and Bit12=Bit11 when accessing the firmware.
The SPIDATA register is restricted to 8bit, so that only each 2nd byte will appear in SPIDATA when attempting to use the bugged-16bit mode.
The NDS Cartridge Slot uses a separate SPI bus (with other I/O Ports), see
The Touch Screen Controller (for lower LCD screen) is accessed via SPI bus,
0-1 Power Down Mode Select
2 Reference Select (0=Differential, 1=Single-Ended)
3 Conversion Mode (0=12bit, max CLK=2MHz, 1=8bit, max CLK=3MHz)
4-6 Channel Select (0-7, see below)
7 Start Bit (Must be set to access Control Byte)
0 Temperature 0 (requires calibration, step 2.1mV per 1'C accuracy)
1 Touchscreen Y-Position (somewhat 0B0h..F20h, or FFFh=released)
2 Battery Voltage (not used, connected to GND in NDS, always 000h)
3 Touchscreen Z1-Position (diagonal position for pressure measurement)
4 Touchscreen Z2-Position (diagonal position for pressure measurement)
5 Touchscreen X-Position (somewhat 100h..ED0h, or 000h=released)
6 AUX Input (connected to Microphone in the NDS)
7 Temperature 1 (difference to Temp 0, without calibration, 2'C accuracy)
All channels can be accessed in Single-Ended mode.
In differential mode, only channel 1,3,4,5 (X,Z1,Z2,Y) can be accessed.
On AK4148AVT, channel 6 (AUX) is split into two separate channels, IN1 and IN2, separated by Bit2 (Reference Select). IN1 is selected when Bit2=1, IN2 is selected when Bit2=0 (despite of the Bit2 settings, both IN1 and IN2 are using single ended more). On the NDS-Lite, IN1 connects to the mircrophone (as on original NDS), and the new IN2 input is simply wired to VDD3.3 (which is equal to the external VREF voltage, so IN2 is always FFFh).
Mode /PENIRQ VREF ADC Recommended use
0 Enabled Auto Auto Differential Mode (Touchscreen, Penirq)
1 Disabled Off On Single-Ended Mode (Temperature, Microphone)
2 Enabled On Off Don't use
3 Disabled On On Don't use
Allows to enable/disable the /PENIRQ output, the internal reference voltage (VREF), and the Analogue-Digital Converter.
For AK4148AVT, Power Down modes are slightly different (among others, /PENIRQ is enabled in Mode 0..2).
VREF is used as reference voltage in single ended mode, at 12bit resolution one ADC step equals to VREF/4096. The TSC generates an internal VREF of 2.5V (+/-0.05V), however, the NDS uses as external VREF of 3.33V (sinks to 3.31V at low battery charge), the external VREF is always enabled, no matter if internal VREF is on or off. Power Down Mode 1 disables the internal VREF, which may reduce power consumption in single ended mode. After conversion, Power Down Mode 0 should be restored to re-enable the Penirq signal.
Switch chipselect low, then output the command byte (MSB first).
The following reply data is received (via Input line) after the Command byte has been transferred: One dummy bit (zero), followed by the 8bit or 12bit conversion result (MSB first), followed by endless padding (zero).
Note: The returned ADC value may become unreliable if there are longer delays between sending the command, and receiving the reply byte(s).
In general, the Output line should be LOW during the reply period, however, once when Data bit6 has been received (or anytime later), a new Command can be invoked (started by sending the HIGH-startbit, ie. Command bit7), simultaneously, the remaining reply-data bits (bit5..0) can be received.
In other words, the new command can be output after receiving 3 bits in 8bit mode (the dummy bit, and data bits 7..6), or after receiving 7 bits in 12bit mode (the dummy bit, and data bits 11..6).
In practice, the NDS SPI register always transfers 8 bits at once, so that one would usually receive 8 bits (rather than above 3 or 7 bits), before outputting a new command.
Read the X and Y positions in 12bit differential mode, then convert the touchscreen values (adc) to screen/pixel positions (scr), as such:
scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1)
scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1)
The X1,Y1 and X2,Y2 calibration points are found in Firmware User Settings,
To calculate the pressure resistance, in respect to X/Y/Z positions and X/Y plate resistances, either of below formulas can be used,
Rtouch = (Rx_plate*Xpos*(Z2pos/Z1pos-1))/4096
Rtouch = (Rx_plate*Xpos*(4096/Z1pos-1)-Ry_plate*(1-Ypos))/4096
The second formula requires less CPU load (as it doesn’t require to measure Z2), the downside is that one must know both X and Y plate resistance (or at least their ratio). The first formula doesn’t require that ratio, and so Rx_plate can be set to any value, setting it to 4096 results in
touchval = Xpos*(Z2pos/Z1pos-1)
Of course, in that case, touchval is just a number, not a resistance in Ohms.
It may be impossible to press locations close to the screen borders.
When pressing two or more locations the TSC values will be somewhere in the middle of these locations.
The TSC values may be garbage if the screen becomes newly pressed or released, to avoid invalid inputs: read TSC values at least two times, and ignore BOTH positions if ONE position was invalid.
Observe that the microphone amplifier is switched off after power up, see:
TP0 decreases by circa 2.1mV per degree Kelvin. The voltage difference between TP1 minus TP0 increases by circa 0.39mV (1/2573 V) per degree Kelvin. At VREF=3.33V, one 12bit ADC step equals to circa 0.8mV (VREF/4096).
Temperature can be calculated at best resolution when using the current TP0 value, and two calibration values (an ADC value, and the corresponding temperature in degrees kelvin):
K = (CAL.TP0-ADC.TP0) * 0.4 + CAL.KELVIN
Alternately, temperature can be calculated at rather bad resolution, but without calibration, by using the difference between TP1 and TP0:
K = (ADC.TP1-ADC.TP0) * 8568 / 4096
To convert Kelvin to other formats,
Celsius: C = (K-273.15)
Fahrenheit: F = (K-273.15)*9/5+32
Reaumur: R = (K-273.15)*4/5
Rankine: X = (K)*9/5
The Temperature Range for the TSC 2046 chip is -40’C..+85’C (for AK4181AVT only -20’C..+70’C). According to Nintendo, the DS should not be exposed to “extreme” heat or cold, the optimal battery charging temperature is specified as +10’C..+40’C.
The original firmware does not support temperature calibration, calibration is supported by nocash firmware (if present). See Extended Settings,
________
VCC 1|o |16 DCLK
X+ 2| |15 /CS
Y+ 3| TSC |14 DIN
X- 4| 2046 |13 BUSY
Y- 5| |12 DOUT
GND 6| |11 /PENIRQ
VBAT 7| |10 IOVDD
AUX 8|________|9 VREF
For AK4181AVT, same pins as above, except that IOVDD replaced by the new IN2 input, the pin is wired to VDD3.3 (so IN2 is always equal to VREF, which is wired to VDD3.3, too) (and AUX is renamed to IN1, and is kept used for MIC input).
DSi in NDS mode does support only X, Y, and MIC (all other channels do return FFFh in 12bit mode, and FFh in 8bit mode, ie. no pressure, no temperature, and no GNDed battery sensor). On DSi, MIC does return data in both single-ended and differential mode (unlike as on real NDS).
The DSi touchscreen controller supports a NDS backwards compatibility mode. But, in DSi mode, it is working entirely different (it’s still accessed via SPI bus, but with some new MODE/INDEX values).
The touchscreen hardware can be switched to NDS compatibility mode (for older games), but unknown how to do that.
The DS contains several Power Managment functions, some accessed via I/O ports (described below), and some accessed via SPI bus:
0 Enable Flag for both LCDs (0=Disable) (Prohibited, see notes)
1 2D Graphics Engine A (0=Disable) (Ports 008h-05Fh, Pal 5000000h)
2 3D Rendering Engine (0=Disable) (Ports 320h-3FFh)
3 3D Geometry Engine (0=Disable) (Ports 400h-6FFh)
4-8 Not used
9 2D Graphics Engine B (0=Disable) (Ports 1008h-105Fh, Pal 5000400h)
10-14 Not used
15 Display Swap (0=Send Display A to Lower Screen, 1=To Upper Screen)
16-31 Not used
Use SwapBuffers command once after enabling Rendering/Geometry Engine.
Improper use of Bit0 may damage the hardware?
When disabled, corresponding Ports become Read-only, corresponding (palette-) memory becomes read-only-zero-filled.
Bit Expl.
0 Sound Speakers (0=Disable, 1=Enable) (Initial setting = 1)
1 Wifi (0=Disable, 1=Enable) (Initial setting = 0)
2-31 Not used
Note: Bit0 disables the internal Speaker only, headphones are not disabled.
Bit1 disables Port 4000206h, and Ports 4800000h-480FFFFh.
Bit Expl.
0-2 Wifi WS0 Control (0-7) (Ports 4800000h-4807FFFh)
3-5 Wifi WS1 Control (0-7) (Ports 4808000h-480FFFFh)
4-15 Not used (zero)
This register is initialized by firmware on power-up, don’t change.
Note: WIFIWAITCNT can be accessed only when enabled in POWCNT2.
In Halt mode, the CPU is paused as long as (IE AND IF)=0.
In Sleep mode, most of the hardware including sound and video are paused, this very-low-power mode could be used much like a screensaver.
Bit Expl.
0-5 Not used (zero)
6-7 Power Down Mode (0=No function, 1=Enter GBA Mode, 2=Halt, 3=Sleep)
The HALTCNT register should not be accessed directly. Instead, the BIOS Halt, Sleep, CustomHalt, IntrWait, or VBlankIntrWait SWI functions should be used.
The NDS7 and NDS9 post boot flags are usually set upon BIOS/Firmware boot completion, once when set the reset vector is redirected to the debug handler of Nintendo’s hardware debugger. That allows the NDS7 debugger to capture accidental jumps to address 0, that appears to be a common problem with HLL-programmers, asm-coders know that (and why) they should not jump to 0.
Bit Expl.
0 Post Boot Flag (0=Boot in progress, 1=Boot completed)
1 NDS7: Not used (always zero), NDS9: Bit1 is read-writeable
2-7 Not used (always zero)
There are some write-restrictions: The NDS7 register can be written to only from code executed in BIOS (done by NDS boot ROM, or by DSi firmware, whereas the DSi firmware is using the CpuSet SWI function to issue the POSTFLG write from within ROM). Bit0 of both NDS7 and NDS9 registers cannot be cleared (except by Reset) once when it is set. DSi games seem to run regardless of POSTFLG, whilst NDS games somewhat refuse to run when POSTFLG=0.
The Power Management Device is accessed via SPI bus,
On Old-DS, registers 4..7Fh are mirrors of 0..3. On DS-Lite, registers 5,6,7 are mirrors of 4, register 8..7Fh are mirrors of 0-7.
On DSi (in DS mode), index 0,1,2,3,4,10h are used (reads as 0Fh,00h,00h,01h,41h,0Fh - regardless of backlight level, and power source), index 5..0Fh and 11h..7Fh return 00h (ie. unlike DS and DS-Lite, there are no mirrors; aside from the mirrored bits in register 10h).
The above bits are essentially used to switch Backlights on or off. However, there a number of strange effects. Backlight dimming is possible by pulse width modulation, ie. by using a timer interrupt to issue pulse widths of N% ON, and 100-N% OFF. Too long pulses are certainly resulting in flickering. Too short pulses are ignored, the backlights will remain OFF, even if the ON and OFF pulses are having the same length. Much too short pulses cause the power supply to shut-down; after changing the backlight state, further changes must not occur within the next (circa) 2500 clock cycles. The mainboard can be operated without screens & backlights connected, however, if so, the power supply will shut-down as soon as backlights are enabled.
Pulse width modulated dimming does also work on the DS-Lite, allowing to use smoother fade in/out effects as when using the five “hardware” levels (Off,Low,Med,High,Max).
The DS Main Memory is 2Mx16bit (4MByte), 1.8V Pseudo SRAM (PSRAM); all Dynamic RAM refresh is handled internally, the chip doesn’t require any external refresh signals, and alltogether behaves like Static RAM. Non-sequential access time is 70ns, sequential (burst) access time is 12ns.
The memory chips contain built-in Control functions, which can be accessed via Port 27FFFFEh and/or by EXMEMCNT Bit 14. Nintendo is using at least two different types of memory chips in DS consoles, Fujitsu 82DBS02163C-70L, and ST M69AB048BL70ZA8, both appear to have different control mechanisms, other chips (with 8MB size) are used in the semi-professional DS hardware debuggers, and further chips may be used in future, so using the memory control functions may lead into compatibitly problems.
Power Consumption during operation (read/write access) is somewhat 30mA, in standby mode (no read/write access) consumption is reduced to 100uA.
Furthermore, a number of power-down modes are supported: In “Deep” Power Down mode the refresh is fully disabled, consumption is 10uA (and all data will be lost), in “Partial” Power Down modes only fragment of memory is refreshed, for smallest fragments, consumption goes to down to circa 50uA. The chip cannot be accessed while it is in Deep or Partial Power Down mode.
The Configuration Register (CR) can be written to by the following sequence:
LDRH R0,[27FFFFEh] ;read one value
STRH R0,[27FFFFEh] ;write should be same value as above
STRH R0,[27FFFFEh] ;write should be same value as above
STRH R0,[27FFFFEh] ;write any value
STRH R0,[27FFFFEh] ;write any value
LDRH R0,[2400000h+CR*2] ;read, address-bits are defining new CR value
Do not access any other Main Memory addresses during above sequence (ie. disable interrupts, and do not execute the sequence by code located in Main Memory). The CR value is write-only. The CR bits are:
Bit Expl.
0-6 Reserved (Must be 7Fh)
7 Write Control
0=WE Single Clock Pulse Control without Write Suspend Function
1=WE Level Control with Write Suspend Function)
Burst Read/Single Write is not supported at WE Single Clock Mode.
8 Reserved (Must be 1)
9 Valid Clock Edge (0=Falling Edge, 1=Rising Edge)
10 Single Write (0=Burst Read/Burst Write, 1=Burst Read/Single Write)
11 Burst Sequence (0=Reserved, 1=Sequential)
12-14 Read Latency (1=3 clocks, 2=4 clocks, 3=5 clocks, other=Reserved)
15 Mode
0=Synchronous: Burst Read, Burst Write
1=Asynchronous: Page Read, Normal Write
In Mode 1 (Async), only the Partial Size bits are used,
all other bits, CR bits 0..18, must be "1".
16-18 Burst Length (2=8 Words, 3=16Words, 7=Continous, other=Reserved)
19-20 Partial Size (0=1MB, 1=512KB, 2=Reserved, 3=Deep/0 bytes)
The Power Down mode is entered by setting CE2=LOW, this can be probably done by setting EXMEMCNT Bit14 to zero.
The chip name decodes as PSRAM (M96), Asynchronous (A), 1.8V Burst (B), 2Mx16 (048), Two Chip Enables (B), Low Leakage (L), 70ns (70), Package (ZA), -30..+85’C (8).
There are three data sheets for different PSRAM chips available at www.st.com (unfortunately none for M69AB048BL70ZA8), each using different memory control mechanisms.
The NDS9 BIOS contains the following Main Memory initialization code, that method doesn’t match up with any ST (nor Fujitsu) data sheets that I’ve seen. At its best, it looks like a strange (and presumably non-functional) mix-up of different ST control methods.
STRH 2000h,[4000204h] ;EXMEMCNT, enable RAM, async mode
LDRH R0,[27FFFFEh]
STRH R0,[27FFFFEh]
STRH R0,[27FFFFEh]
STRH FFDFh,[27FFFFEh]
STRH E732h,[27FFFFEh]
LDRH R0,[27E57FEh]
STRH 6000h,[4000204h] ;EXMEMCNT, enable RAM, normal mode
When booting a 32pin GBA cartridge, the NDS is automatically switched into GBA mode, in that mode all NDS related features are disabled, and the console behaves (almost) like a GBA.
Unlike real GBA, the NDS does not support 8bit DMG/CGB cartridges.
The undocumented Internal Memory Control register (Port 800h) isn’t supported, so the NDS doesn’t allow to use ‘overclocked’ RAM.
The NDS doesn’t have a link-port, so GBA games can be played only in single player mode, link-port accessories cannot be used, and the NDS cannot run GBA code via multiboot.
The CPU, Timers, and Sound Frequencies are probably clocked at 16.76MHz; 33.51MHz/2; a bit slower than the original GBA’s 16.78MHz clock?
In the BIOS, a single byte in a formerly 00h-filled area has been changed from 00h to 01h, resulting in SWI 0Dh returning a different BIOS checksum.
The GBA picture can be shown on upper or lower screen (selectable in boot-menu), the backlight for the selected screen is always on, resulting in different colors & much better visibility than original GBA. Unlike GBA-SP, the NDS doesn’t have a backlight-button.
The GBA screen is centered in the middle of the NDS screen. The surrounding pixels are defined by 32K-color bitmap data in VRAM Block A and B. Each frame, the GBA picture is captured into one block, and is displayed in the next frame (while capturing new data to the other block).
To get a flicker-free border, both blocks should be initialized to contain the same image before entering GBA mode (usually both are zero-filled, resulting in a plain black border).
Note: When using two different borders, the flickering will be irregular - so there appears to be a frame inserted or skipped once every some seconds in GBA mode?!
--- NDS9: ---
ZEROFILL VRAM A,B ;init black screen border (or other color/image)
POWCNT=8003h ;enable 2D engine A on upper screen (0003h=lower)
EXMEMCNT=... ;set Async Main Memory mode (clear bit14)
IME=0 ;disable interrupts
SWI 06h ;halt with interrupts disabled (lockdown)
--- NDS7: ---
POWERMAN.REG0=09h ;enable sound amplifier & upper backlight (05h=lower)
IME=0 ;disable interrupts
wait for VCOUNT=200 ;wait until VBlank
SWI 1Fh with R2=40h ;enter GBA mode, by CustomHalt(40h)
After that, the GBA BIOS will be booted, the GBA Intro will be displayed, and the GBA cartridge (if any) will be started.
4FFFA00h..A0Fh R Emulation ID (16 bytes, eg. "no$gba v2.7", padded with 20h)
4FFFA10h W String Out (raw)
4FFFA14h W String Out (with %param's)
4FFFA18h W String Out (with %param's, plus linefeed)
4FFFA1Ch W Char Out (nocash)
4FFFA20h..A27h R Clock Cycles (64bit)
4FFFA28h..A3Fh - N/A
Note: Above ports can be disabled via the “Debug I/O” option in no$gba setup.
4000640h (32bit) ;aka CLIPMTX_RESULT (mis-used to invoke detection)
4000006h (16bit) ;aka VCOUNT (mis-used to get detection result)
4FFF010h (32bit) ;use to initialize/unlock/reset something
4FFF000h (8bit) ;debug message character output (used when Ensata detected)
The Ensata detection works by mis-using CLIPMTX_RESULT and VCOUNT registers:
[4000640h]=2468ACE0h ;CLIPMTX_RESULT (on real hardware it's read-only)
if ([4000006h] AND 1FFh)=10Eh ;VCOUNT (on real hardware it's 000h..106h)
[4FFF010h]=13579BDFh ;\initialize/reset something
[4FFF010h]=FDB97531h ;/
Ensata=true
else
Ensata=false
endif
Once when a commercial game has detected Ensata, it stops communicating with the ARM7, and instead it does seem to want to communicate with the Ensata executable (which has little to do with real NDS hardware). Ie. aside from “unlocking” port 4FFF000h, it does also “lock” access to the ARM7 hardware (like sound, touchscreen, RTC, etc).
The ISD ports seem to be real (non-emulated) debugging ports, mapped to the GBA Slot region at 8000000h-9FFFFFFh, and used to output text messages, and possible also other debugging stuff.
There are appear to be two variants: nitroemu and cgbemu (the latter appears to be dating back to old 8bit CGB hardware; which was apparently still used for the NDS two hardware generations later).
In Nintendo’s devkit, debug messages are handled in file “os_printf.c”, this file detects the available hardware/software based debug I/O ports, and redirects the [OS_PutString] vector to the corresponding string_out function (eg. to OS_PutStringAris for writing a 00h-terminated string to port 4FFF000h). With some minimal efforts, this could be redirected to the corresponding no$gba debug I/O ports.
Address Bytes Expl.
000h 12 Game Title (Uppercase ASCII, padded with 00h)
00Ch 4 Gamecode (Uppercase ASCII, NTR-<code>) (0=homebrew)
010h 2 Makercode (Uppercase ASCII, eg. "01"=Nintendo) (0=homebrew)
012h 1 Unitcode (00h=NDS, 02h=NDS+DSi, 03h=DSi) (bit1=DSi)
013h 1 Encryption Seed Select (00..07h, usually 00h)
014h 1 Devicecapacity (Chipsize = 128KB SHL nn) (eg. 7 = 16MB)
015h 7 Reserved (zero filled)
01Ch 1 Reserved (zero) (except, used on DSi)
01Dh 1 NDS Region (00h=Normal, 80h=China, 40h=Korea) (other on DSi)
01Eh 1 ROM Version (usually 00h)
01Fh 1 Autostart (Bit2: Skip "Press Button" after Health and Safety)
(Also skips bootmenu, even in Manual mode & even Start pressed)
020h 4 ARM9 rom_offset (4000h and up, align 1000h)
024h 4 ARM9 entry_address (2000000h..23BFE00h)
028h 4 ARM9 ram_address (2000000h..23BFE00h)
02Ch 4 ARM9 size (max 3BFE00h) (3839.5KB)
030h 4 ARM7 rom_offset (8000h and up)
034h 4 ARM7 entry_address (2000000h..23BFE00h, or 37F8000h..3807E00h)
038h 4 ARM7 ram_address (2000000h..23BFE00h, or 37F8000h..3807E00h)
03Ch 4 ARM7 size (max 3BFE00h, or FE00h) (3839.5KB, 63.5KB)
040h 4 File Name Table (FNT) offset
044h 4 File Name Table (FNT) size
048h 4 File Allocation Table (FAT) offset
04Ch 4 File Allocation Table (FAT) size
050h 4 File ARM9 overlay_offset
054h 4 File ARM9 overlay_size
058h 4 File ARM7 overlay_offset
05Ch 4 File ARM7 overlay_size
060h 4 Port 40001A4h setting for normal commands (usually 00586000h)
064h 4 Port 40001A4h setting for KEY1 commands (usually 001808F8h)
068h 4 Icon/Title offset (0=None) (8000h and up)
06Ch 2 Secure Area Checksum, CRC-16 of [[020h]..00007FFFh]
06Eh 2 Secure Area Delay (in 131kHz units) (051Eh=10ms or 0D7Eh=26ms)
070h 4 ARM9 Auto Load List Hook RAM Address (?) ;\endaddr of auto-load
074h 4 ARM7 Auto Load List Hook RAM Address (?) ;/functions
078h 8 Secure Area Disable (by encrypted "NmMdOnly") (usually zero)
080h 4 Total Used ROM size (remaining/unused bytes usually FFh-padded)
084h 4 ROM Header Size (4000h)
088h 4 Unknown, some rom_offset, or zero? (DSi: slightly different)
08Ch 24h Reserved (zero filled; except, [88h..93h] used on DSi)
0B0h 10h Reserved (zero filled; or "DoNotZeroFillMem"=unlaunch fastboot)
0C0h 9Ch Nintendo Logo (compressed bitmap, same as in GBA Headers)
15Ch 2 Nintendo Logo Checksum, CRC-16 of [0C0h-15Bh], fixed CF56h
15Eh 2 Header Checksum, CRC-16 of [000h-15Dh]
160h 4 Debug rom_offset (0=none) (8000h and up) ;only if debug
164h 4 Debug size (0=none) (max 3BFE00h) ;version with
168h 4 Debug ram_address (0=none) (2400000h..27BFE00h) ;SIO and 8MB
16Ch 4 Reserved (zero filled) (transferred, and stored, but not used)
170h 90h Reserved (zero filled) (transferred, but not stored in RAM)
DSi Cartridges are using an extended cartridge header,
DSi Cartridge Header Some of that new/changed DSi header entries are important even in NDS mode:
On DSi, ARM9/ARM7 areas are restricted to 2.75MB (instead 3.8MB on real NDS)
New NDS titles must have RSA signatures (and old titles must be in whitelist)
For more info about CRC-16, see description of GetCRC16 BIOS function,
The Secure Area Delay at header[06Eh] is counted in 130.912kHz units (which can be clocked via one of the hardware timers with prescaler=F/256 and reload=(10000h-((X AND 3FFFh)+2)); for some weird reason, in case of Header checksum it’s ANDed with 1FFFh instead of 3FFFh). Commonly used values are X=051Eh (10ms), and X=0D7Eh (26ms).
The delay is used for all Blowfish encrypted commands, the actual usage/purpose differs depending on bit31 of the ROM Chip ID:
When ChipID.Bit31=0 (commands are sent ONCE): The delay is issued BEFORE sending the command:
Delay,Cmd
Older/newer games are using delays of 10ms/26ms (although all known existing cartridges with Bit31=0 would actually work WITHOUT delays).
When ChipID.Bit31=1 (commands are repeated MULTIPLE times): The delay is issued AFTER sending the command for the FIRST time:
Cmd,Delay,Cmd ;for 2x repeat
Cmd,Delay,Cmd,Cmd,Cmd,Cmd,Cmd,Cmd,Cmd,Cmd ;for 9x repeat
Known games are using delays of 26ms (although all known existing cartridges (=Cooking Coach) with Bit31=1 would actually work with shorter delays of ca. 7ms (but, better use 8ms for safety)).
This is the same code as the NTR-UTTD (NDS) or TWL-UTTD (DSi) code which is printed on the package and sticker on (commercial) cartridges (excluding the leading “NTR-“ or “TWL-“ part).
U Unique Code (usually "A", "B", "C", or special meaning)
TT Short Title (eg. "PM" for Pac Man)
D Destination/Language (usually "J" or "E" or "P" or specific language)
The first character (U) is usually “A” or “B”, in detail:
A NDS common games
B NDS common games
C NDS common games
D DSi-exclusive games
H DSiWare (system utilities and browser) (eg. HNGP=browser)
I NDS and DSi-enhanced games with built-in Infrared port
K DSiWare (dsiware games and flipnote) (eg. KGUV=flipnote)
N NDS nintendo channel demo's japan (NTR-NTRJ-JPN)
T NDS many games
U NDS utilities, educational games, or uncommon extra hardware?
V DSi-enhanced games
Y NDS many games
The second/third characters (TT) are:
Usually an abbreviation of the game title (eg. "PM" for "Pac Man") (unless
that gamecode was already used for another game, then TT is just random)
The fourth character (D) indicates Destination/Language:
A Asian E English/USA I Italian M Swedish Q Danish U Australian
B N/A F French J Japanese N Nor R Russian V EUR+AUS
C Chinese G N/A K Korean O Int S Spanish W..Z Europe #3..5
D German H Dutch L USA #2 P Europe T USA+AUS
The Secure Area is located in ROM at 4000h..7FFFh, it can contain normal program code and data, however, it can be used only for ARM9 boot code, it cannot be used for ARM7 boot code, icon/title, filesystem, or other data.
The Secure Area exists if the ARM9 boot code ROM source address (src) is located within 4000h..7FFFh, if so, it will be loaded (by BIOS via KEY1 encrypted commands) in 4K portions, starting at src, aligned by 1000h, up to address 7FFFh. The secure area size if thus 8000h-src, regardless of the ARM9 boot code size entry in header.
Note: The BIOS silently skips any NDS9 bootcode at src<4000h.
Cartridges with src>=8000h do not have a secure area.
The first 8 bytes of the secure area are containing the Secure Area ID, the ID is required (verified by BIOS boot code), the ID value changes during boot process:
Value Expl.
"encryObj" raw ID before encryption (raw ROM-image)
(encrypted) encrypted ID after encryption (encrypted ROM-image)
"encryObj" raw ID after decryption (verified by BIOS boot code)
E7FFDEFFh,E7FFDEFFh destroyed ID (overwritten by BIOS after verify)
If the decrypted ID does match, then the BIOS overwrites the first 8 bytes by E7FFDEFFh-values (ie. only the ID is destroyed). If the ID doesn’t match, then the first 800h bytes (2K) are overwritten by E7FFDEFFh-values.
The first 2K of the Secure Area (if it exists) are KEY1 encrypted. In official games, this 2K region contains data like so (in decrypted form):
000h..007h Secure Area ID (see above)
008h..00Dh Fixed (FFh,DEh,FFh,E7h,FFh,DEh)
00Eh..00Fh CRC16 across following 7E0h bytes, ie. [010h..7FFh]
010h..7FDh Unknown/random values, mixed with some THUMB SWI calls
7FEh..7FFh Fixed (00h,00h)
Of which, only the ID in the first 8 bytes is verified. Neither BIOS nor (current) firmare versions are verifying the data at 008h..7FFh, so the 7F8h bytes may be also used for normal program code/data.
WLAN files are reportedly same format as cartridges, but without Secure Area, so games with Secure Area cannot be booted via WLAN. No$gba can encrypt and decrypt Secure Areas only if the NDS BIOS-images are present. And, Nintendo’s devkit doesn’t seem to support Secure Area encryption of unreleased games.
So, unencrypted cartridges are more flexible in use. Ways to avoid encryption (which still work on real hardware) are:
1) Set NDS9 ROM offset to 4000h, and leave the first 800h bytes of the Secure Area 00h-filled, which can be (and will be) safely destroyed during loading; due to the missing “encryObj” ID; that method is used by Nintendo’s devkit.
2) Set NDS9 ROM offset to 8000h or higher (cartridge has no Secure Area at all).
3) Set NDS9 ROM offset, RAM address, and size to zero, set NDS7 ROM offset to 200h, and point both NDS9 and NDS7 entrypoints to the loaded NDS7 region. That method avoids waste of unused memory at 200h..3FFFh, and it should be compatible with the NDS console, however, it is not comaptible with commercial cartridges - which do silently redirect address below 4000h to “addr=8000h+(addr AND 1FFh)”. Still, it should work with inofficial flashcards, which do not do that redirection. No$gba emulates the redirection for regular official cartridges, but it disables redirection for homebrew carts if NDS7 rom offset<8000h, and NDS7 size>0.
[One possible problem: Newer “anti-passme” firmware versions reportedly check that the entrypoint isn’t set to 80000C0h, that firmwares might also reject NDS9 entrypoints within the NDS7 bootcode region?]
The ROM offset of the Icon/Title is defined in CartHdr[68h]. The size was originally implied by the size of the original Icon/Title structure rounded to 200h-byte sector boundary (ie. A00h bytes for Version 1 or 2), however, later DSi carts are having a size entry at CartHdr[208h] (usually 23C0h).
If it is present (ie. if CartHdr[68h]=nonzero), then Icon/Title are displayed in the bootmenu.
0000h 2 Version (0001h, 0002h, 0003h, or 0103h)
0002h 2 CRC16 across entries 0020h..083Fh (all versions)
0004h 2 CRC16 across entries 0020h..093Fh (Version 0002h and up)
0006h 2 CRC16 across entries 0020h..0A3Fh (Version 0003h and up)
0008h 2 CRC16 across entries 1240h..23BFh (Version 0103h and up)
000Ah 16h Reserved (zero-filled)
0020h 200h Icon Bitmap (32x32 pix) (4x4 tiles, 4bit depth) (4x8 bytes/tile)
0220h 20h Icon Palette (16 colors, 16bit, range 0000h-7FFFh)
(Color 0 is transparent, so the 1st palette entry is ignored)
0240h 100h Title 0 Japanese (128 characters, 16bit Unicode)
0340h 100h Title 1 English ("")
0440h 100h Title 2 French ("")
0540h 100h Title 3 German ("")
0640h 100h Title 4 Italian ("")
0740h 100h Title 5 Spanish ("")
0840h 100h Title 6 Chinese ("") (Version 0002h and up)
0940h 100h Title 7 Korean ("") (Version 0003h and up)
0A40h 800h Zerofilled (probably reserved for Title 8..15)
Below for animated DSi icons only (Version 0103h and up):
1240h 1000h Icon Animation Bitmap 0..7 (200h bytes each, format as above)
2240h 100h Icon Animation Palette 0..7 (20h bytes each, format as above)
2340h 80h Icon Animation Sequence (16bit tokens)
Unused/padding bytes:
0840h 1C0h Unused/padding (FFh-filled) in Version 0001h
0940h C0h Unused/padding (FFh-filled) in Version 0002h
23C0h 40h Unused/padding (FFh-filled) in Version 0103h
0001h = Original
0002h = With Chinese Title
0003h = With Chinese+Korean Titles
0103h = With Chinese+Korean Titles and animated DSi icon
Usually, for non-multilanguage games, the same (english) title is stored in all title entries. The title may consist of ASCII characters 0020h-007Fh, character 000Ah (linefeed), and should be terminated/padded by 0000h.
The whole text should not exceed the dimensions of the DS cart field in the bootmenu (the maximum number of characters differs due to proportional font).
The title is usually split into a primary title, optional sub-title, and manufacturer, each separated by 000Ah character(s). For example: “America”, 000Ah, “The Axis of War”, 000Ah, “Cynicware”, 0000h.
The sequence is represented by 16bit tokens, in the following format:
15 Flip Vertically (0=No, 1=Yes)
14 Flip Horizontally (0=No, 1=Yes)
13-11 Palette Index (0..7)
10-8 Bitmap Index (0..7)
7-0 Frame Duration (01h..FFh) (in 60Hz units)
Value 0000h indicates the end of the sequence. If the first token is 0000h, then the non-animated default image is shown.
Uh, actually, a non-animated icon uses values 01h,00h,00h,01h, followed by 7Ch zerofilled bytes (ie. 0001h, 0100h, 3Eh x 0000h)?
Some DSi games are having a separate “banner.sav” file stored in the eMMC filesystem, enabled via carthdr[1BFh].bit2 (allowing to indicate the game progress by overriding the default icon). The banner files are 4000h bytes in size, the animation data is same as above, but without title strings and without non-animated icon.
0000h 2 Version (0103h)
0002h 6 Reserved (zero-filled)
0008h 2 CRC16 across entries 0020h..119Fh (with initial value FFFFh)
000Ah 16h Reserved (zero-filled)
0020h 1000h Icon Animation Bitmap 0..7 (200h bytes each) ;\same format as
1020h 100h Icon Animation Palette 0..7 (20h bytes each) ; in Icon/Title
1120h 80h Icon Animation Sequence (16bit tokens) ;/
11A0h 2E60h Garbage (random values, maybe due to eMMC decryption)
The feature is used by some Brain Age Express games (for example, Brain Age Express Sudoku: ‘title\00030004\4b4e3945\data\banner.sav’).
The feature does probably work only for DSiware titles (unless there are any DSi carts with SD/MMC access enabled; or unless there is a feature for storing similar data in cartridge memory).
Communication with Cartridge ROM relies on sending 8 byte commands to the cartridge, after the sending the command, a data stream can be received from the cartridge (the length of the data stream isn’t fixed, below descriptions show the default length in brackets, but one may receive more, or less bytes, if desired).
0000000h-0000FFFh Header (unencrypted)
0001000h-0003FFFh Not read-able (zero filled in ROM-images)
0004000h-0007FFFh Secure Area, 16KBytes (first 2Kbytes with extra encryption)
0008000h-... Main Data Area
DSi cartridges are split into a NDS area (as above), and a new DSi area:
XX00000h XX02FFFh DSi Not read-able (XX00000h=first megabyte after NDS area)
XX03000h-XX06FFFh DSi ARM9i Secure Area (usually with modcrypt encryption)
XX07000h-... DSi Main Data Area
Cartridge memory must be copied to RAM (the CPU cannot execute code in ROM).
Command/Params Expl. Cmd Reply Len
-- Unencrypted Load --
9F00000000000000h Dummy (read HIGH-Z bytes) RAW RAW 2000h
0000000000000000h Get Cartridge Header RAW RAW 200h DSi:1000h
9000000000000000h 1st Get ROM Chip ID RAW RAW 4
A000000000000000h Get 3DS encryption type (3DS) RAW RAW 4
00aaaaaaaa000000h Unencrypted Data (debug ver only) RAW RAW 200h
3Ciiijjjxkkkkkxxh Activate KEY1 Encryption (NDS) RAW RAW 0
3Diiijjjxkkkkkxxh Activate KEY1 Encryption (DSi) RAW RAW 0
3E00000000000000h Activate 16-byte commands (3DS) RAW RAW 0
-- Secure Area Load --
4llllmmmnnnkkkkkh Activate KEY2 Encryption Mode KEY1 FIX 910h+0
1lllliiijjjkkkkkh 2nd Get ROM Chip ID KEY1 KEY2 910h+4
xxxxxxxxxxxxxxxxh Invalid - Get KEY2 Stream XOR 00h KEY1 KEY2 910h+...
2bbbbiiijjjkkkkkh Get Secure Area Block (4Kbytes) KEY1 KEY2 910h+10A8h
6lllliiijjjkkkkkh Optional KEY2 Disable KEY1 KEY2 910h+?
Alllliiijjjkkkkkh Enter Main Data Mode KEY1 KEY2 910h+0
-- Main Data Load --
B7aaaaaaaa000000h Encrypted Data Read KEY2 KEY2 200h
B800000000000000h 3rd Get ROM Chip ID KEY2 KEY2 4
xxxxxxxxxxxxxxxxh Invalid - Get KEY2 Stream XOR 00h KEY2 KEY2 ...
B500000000000000h Whatever NAND related? (DSi?) KEY2 KEY2 0
D600000000000000h Whatever NAND related? (DSi?) KEY2 KEY2 4
The parameter digits contained in above commands are:
aaaaaaaa 32bit ROM address (command B7 can access only 8000h and up)
bbbb Secure Area Block number (0004h..0007h for addr 4000h..7000h)
x,xx Random, not used in further commands (DSi: always zero)
iii,jjj,llll Random, must be SAME value in further commands
kkkkk Random, must be INCREMENTED after FURTHER commands
mmm,nnn Random, used as KEY2-encryption seed
++++ Unencrypted Commands (First Part of Boot Procedure) ++++
The /RES Pin switches the cartridge into unencrypted mode. After reset, the first two commands (9Fh and 00h) are transferred at 4MB/s CLK rate.
Dummy command send after reset, returns endless stream of HIGH-Z bytes (ie. usually receiving FFh, immediately after sending the command, the first 1-2 received bytes may be equal to the last command byte).
Returns RAW unencrypted cartridge header, repeated every 1000h bytes. The interesting area are the 1st 200h bytes, the rest is typically zero filled (except on DSi carts, which do use the whole 1000h bytes).
The Gamecode header entry is used later on to initialize the encryption. Also, the ROM Control entries define the length of the KEY1 dummy periods (typically 910h clocks), and the CLK transfer rate for further commands (typically faster than the initial 4MB/s after power up).
Returns RAW unencrypted Chip ID (eg. C2h,0Fh,00h,00h), repeated every 4 bytes.
1st byte - Manufacturer (eg. C2h=Macronix) (roughly based on JEDEC IDs)
2nd byte - Chip size (00h..7Fh: (N+1)Mbytes, F0h..FFh: (100h-N)*256Mbytes?)
3rd byte - Flags (see below)
4th byte - Flags (see below)
The Flag Bits in 3th byte can be
0 Uses Infrared (but via SPI, unrelated to ROM) (also Jam with the Band)
1 Unknown (set in some 3DS carts)
2-6 Zero
7 Unknown (set in Kingdom Hearts - Re-Coded)
The Flag Bits in 4th byte can be
0-2 Zero
3 Seems to be NAND flag (0=ROM, 1=NAND) (observed in only ONE cartridge)
4 3DS Flag (0=NDS/DSi, 1=3DS)
5 Zero ... set in ... DSi-exclusive games? (support cmd B5h/D6h ?)
6 DSi flag (0=NDS/3DS, 1=DSi) (but also set in NDS Walk with Me)
7 Cart Protocol Variant (0=older/smaller carts, 1=newer/bigger carts)
Existing/known ROM IDs are:
C2h,07h,00h,00h NDS Macronix 8MB ROM (eg. DS Vision)
AEh,0Fh,00h,00h NDS Noname 16MB ROM (eg. Meine Tierarztpraxis)
C2h,0Fh,00h,00h NDS Macronix 16MB ROM (eg. Metroid Demo)
C2h,1Fh,00h,00h NDS Macronix 32MB ROM (eg. Over the Hedge)
C2h,1Fh,00h,40h DSi Macronix 32MB ROM (eg. Art Academy, TWL-VAAV, SystemFlaw)
80h,3Fh,01h,E0h NDS SanDisk 64MB ROM+Infrared (eg. Walk with Me, NTR-IMWP)
AEh,3Fh,00h,E0h DSi Noname 64MB ROM (eg. de Blob 2, TWL-VD2V)
C2h,3Fh,00h,00h NDS Macronix 64MB ROM (eg. Ultimate Spiderman)
C2h,3Fh,00h,40h DSi Macronix 64MB ROM (eg. Crime Lab, NTR-VAOP)
80h,7Fh,00h,80h NDS SanDisk 128MB ROM (DS Zelda, NTR-AZEP-0)
80h,7Fh,01h,E0h ? SanDisk? 128MB ROM+Infrared? (P-letter SoulSilver, IPGE)
C2h,7Fh,00h,80h NDS Macronix 128MB ROM (eg. Spirit Tracks, NTR-BKIP)
C2h,7Fh,00h,C0h DSi Macronix 128MB ROM (eg. Cooking Coach, TWL-VCKE)
ECh,7Fh,00h,88h NDS Samsung 128MB NAND (eg. Warioware D.I.Y.)
ECh,7Fh,01h,88h NDS Samsung? 128MB NAND+What? (eg. Jam with the Band, UXBP)
ECh,7Fh,00h,E8h DSi Samsung? 128MB NAND (eg. Face Training, USKV)
80h,FFh,80h,E0h NDS SanDisk? 256MB ROM (Kingdom Hearts - Re-Coded, NTR-BK9P)
C2h,FFh,01h,C0h DSi Macronix 256MB ROM+Infrared? (eg. P-Letter White)
C2h,FFh,00h,80h NDS Macronix 256MB ROM (eg. Band Hero, NTR-BGHP)
C2h,FEh,01h,C0h DSi Macronix 512MB ROM+Infrared? (eg. P-Letter White 2)
C2h,FEh,00h,90h 3DS Macronix probably 512MB? ROM (eg. Sims 3)
45h,FAh,00h,90h 3DS ?? maybe... 1.5GB? ROM (eg. Starfox)
C2h,F8h,00h,90h 3DS Macronix maybe... 2GB? ROM (eg. Kid Icarus)
C2h,7Fh,00h,90h 3DS Macronix 128MB ROM CTR-P-AENJ MMinna no Ennichi
C2h,FFh,00h,90h 3DS Macronix 256MB ROM CTR-P-AFSJ Pro Yakyuu Famista 2011
C2h,FEh,00h,90h 3DS Macronix 512MB ROM CTR-P-AFAJ Real 3D Bass FishingFishOn
C2h,FAh,00h,90h 3DS Macronix 1GB ROM CTR-P-ASUJ Hana to Ikimono Rittai Zukan
C2h,FAh,02h,90h 3DS Macronix 1GB ROM CTR-P-AGGW Luigis Mansion 2 ASiA CHT
C2h,F8h,00h,90h 3DS Macronix 2GB ROM CTR-P-ACFJ Castlevania - Lords of Shadow
C2h,F8h,02h,90h 3DS Macronix 2GB ROM CTR-P-AH4J Monster Hunter 4
AEh,FAh,00h,90h 3DS Noname? 1GB ROM CTR-P-AGKJ Gyakuten Saiban 5
AEh,FAh,00h,98h 3DS Noname? 1GB NAND CTR-P-EGDJ Tobidase Doubutsu no Mori
45h,FAh,00h,90h 3DS ?? 1GB ROM CTR-P-AFLJ Fantasy Life
45h,F8h,00h,90h 3DS ?? 2GB ROM CTR-P-AVHJ Senran Kagura Burst - Guren
C2h,F0h,00h,90h 3DS Macronix 4GB ROM CTR-P-ABRJ Biohazard Revelations
FFh,FFh,FFh,FFh None (no cartridge inserted)
The Samsung NAND chip appears to use a slightly different protocol (seems as if it allows to read ROM header and ID only once, or as if it gets confused when reading more than 4 ID bytes, or so) (and of course, the protocol is somehow extended, allowing to write data to the NAND memory). The official JEDEC ID for Samsung would be “CEh”, but for some reason, Samsung’s NDS chip does spit out “ECh” as Maker ID.
The 3Ch command returns endless stream of HIGH-Z bytes, all following commands, and their return values, are encrypted. The random parameters iii,jjj,kkkkk must be re-used in further commands; the 20bit kkkkk value is to be incremented by one after each <further> command (it is <not> incremented after the 3Ch command).
Same as command 3Ch (but with different initial 1048h-byte encryption values), and works only on DSi carts. Command 3Dh is unlocking two features on DSi carts:
1) Command 2bbbbiiijjjkkkkkh loads ARM9i secure area (instead of ARM9 area)
2) Command B7aaaaaaaa000000h allows to read the 'whole' cartridge space
Without command 3Dh, DSi carts will allow to read only the first some megabytes (for example, the first 11 Mbyte of the System Flaw cartridge), and the remaining memory returns mirrors of “addr=8000h+(addr AND 1FFh)”).
Note: After reset, the cartridge protocol allows to send only either one of the 3Ch/3Dh commands (DSi consoles can control the cartridge reset pin, so they can first send 3Ch and read the normal secure area, then issue a reset and 3Dh and read the DSi secure area) (on a NDS one could do the same by ejecting/inserting the cartridge instead of toggling the reset pin).
++++ KEY1 Encrypted Commands (2nd Part of Boot procedure) ++++
KEY1 encrypted command, parameter mmmnnn is used to initialize the KEY2 encryption stream. Returns 910h dummy bytes (which are still subject to old KEY2 settings; at pre-initialization time, this is fixed: HIGH-Z, C5h, 3Ah, 81h, etc.). The new KEY2 seeds are then applied, and the first KEY2 byte is then precomputed. The 910h dummy stream is followed by that precomputed byte value endless repeated (this is the same value as that “underneath” of the first HIGH-Z dummy-byte of the next command).
Secure1000h: Returns repeated FFh bytes (instead of the leading C5h, 3Ah, 81h, etc. stuff).
Secure1000h: Returns repeated FFh bytes (instead of the repeated precomputed value).
KEY1 encrypted command. Returns 910h dummy bytes, followed by KEY2 encrypted Chip ID repeated every 4 bytes, which must be identical as for the 1st Get ID command. The BIOS randomly executes this command once or twice. Changing the first command byte to any other value returns an endless KEY2 encrypted stream of 00h bytes, that is the easiest way to retrieve encryption values and to bypass the copyprotection.
KEY1 encrypted command. Used to read a secure area block (bbbb in range 0004h..0007h for addr 4000h..7000h) (or, after sending command 3Dh on a DSi: bbbb in range 0004h..0007h for addr XX03000h..XX06000h).
Each block is 4K, so it requires four Get Secure Area commands to receive the whole Secure Area (ROM locations 4000h-7FFFh), the BIOS is reading these blocks in random order.
Normally (if the upper bit of the Chip ID is set): Returns 910h dummy bytes, followed by 200h KEY2 encrypted Secure Area bytes, followed by 18h KEY2 encrypted 00h bytes, then the next 200h KEY2 encrypted Secure Area bytes, again followed by 18h KEY2 encrypted 00h bytes, and so on. That stream is repeated every 10C0h bytes (8x200h data bytes, plus 8x18h zero bytes).
Alternately (if the upper bit of the Chip ID is zero): Returns 910h dummy bytes, followed by 1000h KEY2 encrypted Secure Area bytes, presumably followed by 18h bytes, too.
Aside from above KEY2 encryption (which is done by hardware), the first 2K of the NDS Secure Area is additionally KEY1 encrypted; which must be resolved after transfer by software (and the DSi Secure Area is usually modcrypted, as specified in the cartridge header).
KEY1 encrypted command. Returns 910h dummy bytes (which are still KEY2 affected), followed by endless stream of RAW 00h bytes. KEY2 encryption is disabled for all following commands.
This command is send only if firmware[18h] matches encrypted string “enPngOFF”, and ONLY if firmware get_crypt_keys had completed BEFORE completion of secure area loading, this timing issue may cause unstable results.
KEY1 encrypted command. Returns 910h dummy bytes, followed by endless KEY2 encrypted stream of 00h bytes. All following commands are KEY2 encrypted.
++++ KEY2 Encrypted Commands (Main Data Transfer) ++++
KEY2 encrypted command. The desired ROM address is specifed, MSB first, in parameter bytes (a). Returned data is KEY2 encrypted.
There is no alignment restriction for the address. However, the datastream wraps to the begin of the current 4K block when address+length crosses a 4K boundary (1000h bytes). Special case: SanDisk ROMs are forcefully 200h-byte aligned, and can merely read max 200h bytes (padded with unencrypted FFh bytes when trying to read more data).
The command can be used only for addresses 8000h and up. Addresses 0..7FFFh are silently redirected to address “8000h+(addr AND 1FFh)”. DSi cartridges will also reject XX00000h..XX06FFFh in the same fashion (and also XX07000h and up if the DSi cartridge isn’t unlocked via command 3Dh).
Addresses that do exceed the ROM size do mirror to the valid address range (that includes mirroring non-loadable regions like 0..7FFFh to “8000h+(addr AND 1FFh)”; some newer games are using this behaviour for some kind of anti-piracy checks).
KEY2 encrypted command. Returns KEY2 encrypted Chip ID repeated every 4 bytes.
Any other command (anything else than above B7h and B8h) in KEY2 command mode causes communcation failures. The invalid command returns an endless KEY2 encrypted stream of 00h bytes. After the invalid command, the KEY2 stream is NOT advanced for further command bytes, further commands seems to return KEY2 encrypted 00h bytes, of which, the first returned byte appears to be HIGH-Z.
Ie. the cartridge seems to have switched back to a state similar to the KEY1-phase, although it doesn’t seem to be possible to send KEY1 commands.
++++ Notes ++++
All KEY1 encrypted commands are followed by 910h dummy byte transfers, these 910h clock cycles are probably used to decrypt the command at the cartridge side; communication will fail when transferring less than 910h bytes.
The return values for the dummy transfer are: A single HIGH-Z byte, followed by 90Fh KEY2-encrypted 00h bytes. The KEY2 encryption stream is advanced for all 910h bytes, including for the HIGH-Z byte.
Note: Current cartridges are using 910h bytes, however, other carts might use other amounts of dummy bytes, the 910h value can be calculated based on ROM Control entries in cartridge header. For the KEY1 formulas, see:
There are two protocol variants for NDS carts, indicated by Bit31 of the ROM Chip ID (aka bit7 of the 4th ID byte):
1) Chip ID.Bit31=0 Used by older/smaller carts with up to 64MB ROM
2) Chip ID.Bit31=1 Used by newer/bigger carts with 64MB or more ROM
The first variant (for older carts) is described above. The second second variant includes some differences for KEY1 encrypted commands:
GAPS: The commands have the same 910h-cycle gaps, but without outputting CLK pulses during those gaps (ie. used with ROMCTRL.Bit28=0) (the absence of the CLKs implies that there is no dummy data transferred during gaps, and accordingly, that the KEY2 stream isn’t advanced during the 910h gap cycles).
REPEATED COMMANDS and SECURE AREA DELAY: All KEY1 encrypted commands must be sent TWICE (or even NINE times). First, send the command with 0-byte Data transfer length. Second, issue the Secure Area Delay (required; use the delay specified in cart header[06Eh]).
Third, send the command once again with 0-byte or 4-byte data transfer length (usually 0 bytes, or 4-bytes for Chip ID command), or sent it eight times with 200h-byte data transfer length (for the 1000h-byte secure area load command).
For those repeats, always resend exactly the same command (namely, kkkkk is NOT incremented during repeats, and there is no extra index needed to select 200h-byte portions within 1000h-byte blocks; the cartridge is automatically outputting the eight portions one after another).
Type Total Size Page Size Chip/Example Game/Example
EEPROM 0.5K bytes 16 bytes ST M95040-W (eg. Metroid Demo)
EEPROM 8K bytes 32 bytes ST M95640-W (eg. Super Mario DS)
EEPROM 64K bytes 128 bytes ST M95512-W (eg. Downhill Jam)
EEPROM 128K bytes ? bytes ? (eg. Explorers of Sky)
FLASH 256K bytes 256 bytes ST M45PE20 (eg. Skateland)
FLASH 256K bytes Sanyo LE25FW203T (eg. Mariokart)
FLASH 512K bytes 256 bytes ST M25PE40? (eg. which/any games?)
FLASH 512K bytes ST 45PE40V6 (eg. DS Zelda, NTR-AZEP-0)
FLASH 1024K bytes ST 45PE80V6 (eg. Spirit Tracks, NTR-BKIP)
FLASH 8192K bytes MX25L6445EZNI-10G (Art Academy only, TWL-VAAV)
FRAM 8K bytes No limit ? (eg. which/any games?)
FRAM 32K bytes No limit Ramtron FM25L256? (eg. which/any games?)
Type Max Writes per Page Data Retention
EEPROM 100,000 40 years
FLASH 100,000 20 years
FRAM No limit 10 years
SPI Bus Backup Memory is accessed via Ports 40001A0h and 40001A2h, see
For all EEPROM and FRAM types:
06h WREN Write Enable Cmd, no parameters
04h WRDI Write Disable Cmd, no parameters
05h RDSR Read Status Register Cmd, read repeated status value(s)
01h WRSR Write Status Register Cmd, write one-byte value
9Fh RDID Read JEDEC ID (not supported on EEPROM/FLASH, returns FFh-bytes)
For 0.5K EEPROM (8+1bit Address):
03h RDLO Read from Memory 000h-0FFh Cmd, addr lsb, read byte(s)
0Bh RDHI Read from Memory 100h-1FFh Cmd, addr lsb, read byte(s)
02h WRLO Write to Memory 000h-0FFh Cmd, addr lsb, write 1..MAX byte(s)
0Ah WRHI Write to Memory 100h-1FFh Cmd, addr lsb, write 1..MAX byte(s)
For 8K..64K EEPROM and for FRAM (16bit Address):
03h RD Read from Memory Cmd, addr msb,lsb, read byte(s)
02h WR Write to Memory Cmd, addr msb,lsb, write 1..MAX byte(s)
For 128K EEPROM (24bit Address):
As above, but with 24bit addr msb,mid,lsb ?
Note: MAX = Page Size (see above chip list) (no limit for FRAM).
For FLASH backup, commands should be same as for Firmware FLASH memory:
DS Firmware Serial Flash Memory A few NDS/DSi carts are sharing the SPI bus for FLASH and Infrared, this requires a 00h-prefix byte for FLASH access, with slower 1MHz SPI clock and delays, see:
0 WIP Write in Progress (1=Busy) (Read only) (always 0 for FRAM chips)
1 WEL Write Enable Latch (1=Enable) (Read only, except by WREN,WRDI)
2-3 WP Write Protect (0=None, 1=Upper quarter, 2=Upper Half, 3=All memory)
For 0.5K EEPROM:
4-7 ONEs Not used (all four bits are always set to "1" each)
For 8K..64K EEPROM and for FRAM:
4-6 ZERO Not used (all three bits are always set to "0" each)
7 SRWD Status Register Write Disable (0=Normal, 1=Lock) (Only if /W=LOW)
WEL gets reset on Power-up, WRDI, WRSR, WRITE/LO/HI, and on /W=LOW.
The WRSR command allows to change ONLY the two WP bits, and the SRWD bit (if any), these bits are non-volatile (remain intact during power-down), respectively, the WIP bit must be checked to sense WRSR completion.
The overall memory type and bus-width can be detected by RDSR/RDID commands:
RDSR RDID Type (bus-width)
FFh, FFh,FFh,FFh None (none)
F0h, FFh,FFh,FFh EEPROM (with 8+1bit address bus)
00h, FFh,FFh,FFh EEPROM/FRAM (with 16bit address bus)
? ?,?,? EEPROM (with 24bit address bus)
00h, xxh,xxh,xxh FLASH (usually with 24bit address bus)
And, the RD commands can be used to detect the memory size/mirrors (though that won’t work if the memory is empty).
Nintendo is using different functions for sending cmd+addr and data. The bus-width can be detected by counting the bytes transferred with same program counter after chip selection. One could also try to examine code/data in the ROM-image (but that may envolve self-decompressing code and other obstacles).
Special cases:
Over the Hedge does initially try to access 8Kbyte EEPROM, but does actually use 0.5Kbyte EEPROM (as workaround: re-detect the bus-width on each transfer).
Rune Factory - A Fantasy Harvest Moon seems to be also difficult to detect (as workaround: force 64Kbyte EEPROM on gamecode ARFx).
FLASH has same 24bit bus-width as 128Kbyte EEPROM, but isn’t compatible on writing. EEPROM does use 02h=Write+Erase. FLASH does use 0Ah=Write+Erase (or D8h/DBh=Erase and 02h=Write separatly).
Pin Name Expl.
1 /S Chip Select
2 Q Data Out
3 /W Write-Protect (not used in NDS, wired to VCC)
4 VSS Ground
5 D Data In
6 C Clock
7 /HOLD Transfer-pause (not used in NDS, wired to VCC)
8 VCC Supply 2.5 to 5.5V for M95xx0-W
FRAM (Ferroelectric Nonvolatile RAM) is fully backwards compatible with normal EEPROMs, but comes up with faster write/erase time (no delays), and with lower power consumption, and unlimited number of write/erase cycles. Unlike as for normal RAM, as far as I understand, the data remains intact without needing any battery.
DS Vision (NDS cart with microSD slot... and maybe ALSO with EEPROM?)
Warioware D.I.Y. (uses a single NAND FLASH chip for both 'ROM' and 'SAVE')
(the warioware chip is marked "SAMSUNG 004, KLC2811ANB-P204, NTR-UORE-0")
(the warioware PCB is marked "DI X-7 C17-01")
and, a few games are said to have "Flash - 64 Mbit" save memory?
DSi cartridges are usually (maybe always) having SD/MMC access disabled, so they must stick using EEPROM/FLASH chips inside of the cartridges (which is required for NDS compatibility anyways).
However, DSiware games (downloaded from DSi Shop) are allowed to save data on eMMC, using “private.sav” or “public.sav” files in their data folder. The size of that files is preset in cartridge header.
The Gamecard bus registers can be mapped to NDS7 or NDS9 via EXMEMCNT, see
0-1 SPI Baudrate (0=4MHz/Default, 1=2MHz, 2=1MHz, 3=512KHz)
2-5 Not used (always zero)
6 SPI Hold Chipselect (0=Deselect after transfer, 1=Keep selected)
7 SPI Busy (0=Ready, 1=Busy) (presumably Read-only)
8-12 Not used (always zero)
13 NDS Slot Mode (0=Parallel/ROM, 1=Serial/SPI-Backup)
14 Transfer Ready IRQ (0=Disable, 1=Enable) (for ROM, not for AUXSPI)
15 NDS Slot Enable (0=Disable, 1=Enable) (for both ROM and AUXSPI)
The “Hold” flag should be cleared BEFORE transferring the LAST data unit, the chipselect will be then automatically cleared after the transfer, the program should issue a WaitByLoop(12) on NDS7 (or longer on NDS9) manually AFTER the LAST transfer.
The SPI transfer is started on writing to this register, so one must <write> a dummy value (should be zero) even when intending to <read> from SPI bus.
0-7 Data
8-15 Not used (always zero)
During transfer, the Busy flag in AUXSPICNT is set, and the written DATA value is transferred to the device (via output line), simultaneously data is received (via input line). Upon transfer completion, the Busy flag goes off, and the received value can be then read from AUXSPIDATA, if desired.
0-12 KEY1 gap1 length (0-1FFFh) (forced min 08F8h by BIOS) (leading gap)
13 KEY2 encrypt data (0=Disable, 1=Enable KEY2 Encryption for Data)
14 "SE" Unknown? (usually same as Bit13) (does NOT affect timing?)
15 KEY2 Apply Seed (0=No change, 1=Apply Encryption Seed) (Write only)
16-21 KEY1 gap2 length (0-3Fh) (forced min 18h by BIOS) (200h-byte gap)
22 KEY2 encrypt cmd (0=Disable, 1=Enable KEY2 Encryption for Commands)
23 Data-Word Status (0=Busy, 1=Ready/DRQ) (Read-only)
24-26 Data Block size (0=None, 1..6=100h SHL (1..6) bytes, 7=4 bytes)
27 Transfer CLK rate (0=6.7MHz=33.51MHz/5, 1=4.2MHz=33.51MHz/8)
28 KEY1 Gap CLKs (0=Hold CLK High during gaps, 1=Output Dummy CLK Pulses)
29 RESB Release Reset (0=Reset, 1=Release) (cannot be cleared once set)
30 "WR" Unknown, maybe data-write? (usually 0) (read/write-able)
31 Block Start/Status (0=Ready, 1=Start/Busy) (IRQ See 40001A0h/Bit14)
The cartridge header is booted at 4.2MHz CLK rate, and following transfers are then using ROMCTRL settings specified in cartridge header entries [060h] and [064h], which are usually using 6.7MHz CLK rate for the main data transfer phase (whereof, older MROM carts can actually transfer 6.7Mbyte/s, but newer 1T-ROM carts default to reading 200h-byte blocks with gap1=657h, thus reaching only 1.6Mbyte/s).
Transfer length of null, four, and 200h..4000h bytes are supported by the console, however, retail cartridges cannot cross 1000h-byte boundaries (and, SanDisk ROMs cannot transfer more than 200h bytes).
hdr[60h] hdr[64h] hdr[6Eh]
00586000h 001808F8h 051Eh ;older/faster MROM
00416657h 081808F8h 0D7Eh ;newer/slower 1T-ROM
? ? ? ;whatever NAND
The romctrl values in cartheader[60h,64h] are okay, but the secure delay in [6Eh] is nonsense (should be zero).
Some carts like SystemFlaw and BiggestLoser are actually containing MROM chips despite of having 1T-ROM values in cart header (gap1=657h is making loading insane slow, gap2=01h causes errors on 1000h-byte blocks, and secure.clk=4.2MHz is slowing down secure area loading, combined with even slower secure area delay despite of not needing any delay for MROM).
As the cart header entries are wrong, some other detection is needed: This can be probably done by checking ChipID.bit31 (or otherwise by testing if 1000h-block reading works with gap1=01h, if so, then it’s 1T-ROM).
Actual 1T-ROM carts can be very slow, especially when using the insane cart header values and default firmware blocksize of 200h bytes which drops loading speed from 6.7Mbytes/s to 1.6Mbyte/s (as workaround, use gap1=180h, blocksize=1000h, also secure area delay should be 400h, not D7Eh) (tested/working for CookingCoach, unknown if that timings work for all other carts).
Some cartridges are said to contain NAND memory, unknown if that’s accessed with the normal ROM reading commands, and if so, with which timings.
Reset flag in bit29 can be set once only (to release reset), the only way to clear the bit is power-off. However, there are some ways to issue resets:
1) On NDS: Manually eject/insert the cart (that won’t affect bit29, but the cart will reset itself anyways upon power loss) (eject on DSi will power-off the cart slot).
2) If one of the two ROMCTRL registers (on ARM7 and ARM9) is still zero: Temporarily toggle ARM7/ARM9 cart access via EXMEMCNT on ARM9 side.
3) On DSi: If the 2nd cart slot ROMCTRL register (40021A4h) is still zero: Temporarily swap 1ns/2nd cart slot via SCFG_MC.bit15 on ARM7 side.
4) On DSi: Use SCFG_MC to toggle cart power off/on; this will actually reset bit29, the DSi firmware is actually using that method, but it’s very slow (takes about 300ms, for the power-off wait, plus (unneccassary) hardcoded power-on delays).
The separate commands are described in the Cartridge Protocol chapter, however, once when the BIOS boot procedure has completed, one would usually only need command “B7aaaaaaaa000000h”, for reading data (usually 200h bytes) from address aaaaaaaah (which should be usually aligned by 200h).
0-7 1st Command Byte (at 40001A8h) (eg. B7h) (MSB)
8-15 2nd Command Byte (at 40001A9h) (eg. addr bit 24-31)
16-23 3rd Command Byte (at 40001AAh) (eg. addr bit 16-23)
24-31 4th Command Byte (at 40001ABh) (eg. addr bit 8-15) (when aligned=even)
32-39 5th Command Byte (at 40001ACh) (eg. addr bit 0-7) (when aligned=00h)
40-47 6th Command Byte (at 40001ADh) (eg. 00h)
48-57 7th Command Byte (at 40001AEh) (eg. 00h)
56-63 8th Command Byte (at 40001AFh) (eg. 00h) (LSB)
Observe that the command/parameter MSB is located at the smallest memory location (40001A8h), ie. compared with the CPU, the byte-order is reversed.
0-7 1st received Data Byte (at 4100010h)
8-15 2nd received Data Byte (at 4100011h)
16-23 3rd received Data Byte (at 4100012h)
24-31 4th received Data Byte (at 4100013h)
After sending a command, data can be read from this register manually (when the DRQ bit is set), or by DMA (with DMASAD=4100010h, Fixed Source Address, Length=1, Size=32bit, Repeat=On, Mode=DS Gamecard).
These registers are used by the NDS7 BIOS to initialize KEY2 encryption (and there’s normally no need to change that initial settings). Writes to the Seed registers do not have direct effect on the internal encryption registers, until the Seed gets applied by writing “1” to ROMCTRL.Bit15.
For more info:
DS Encryption by Random Seed (KEY2) Note: There are <separate> Seed registers for both NDS7 and NDS9, which can be applied by ROMCTRL on NDS7 and NDS9 respectively (however, once when applied to the internal registers, the new internal setting is used for <both> CPUs).
The DS hardware, BIOS, and Firmware do NOT contain any built-in filesystem functions. The ARM9/ARM7 boot code (together max 3903KB), and Icon/Title information are automatically loaded on power-up.
Programs that require to load additional data from cartridge ROM may do that either by implementing whatever functions to translate filenames to ROM addresses, or by reading from ROM directly.
The NitroROM Filesystem is used by many NDS games (at least those that have been developed with Nintendo’s tools). It’s used for ROM Cartridges, and, on the DSi, it’s also used for DSiWare games (in the latter case, NitroROM acts as a 2nd virtual filesystem inside of the DSi’s FAT16 filesystem).
FNT = cart_hdr[040h] ;\origin as defined in ROM cartridge header
FAT = cart_hdr[048h] ;/
IMG = 00000000h ;-origin at begin of ROM
Aside from using filenames, NitroROM files can be alternately accessed via Overlay IDs (see later on below).
NARC Files are often found inside of NitroROM Filesystems (ie. NARC is a second virtual filesystem, nested inside of the actual filesystem). The NARC Format is very similar to the NitroROM Format, but with additional Chunk Headers (instead of the Cartridge ROM Header).
... ... Optional Header (eg. compression header, or RSA signature)
000h 4 Chunk Name "NARC" (Nitro Archive) ;\
004h 2 Byte Order (FFFEh) (unlike usually, not FEFFh) ;
006h 2 Version (0100h) ; NARC
008h 4 File Size (from "NARC" ID to end of file) ; Header
00Ch 2 Chunk Size (0010h) ;
00Eh 2 Number of following chunks (0003h) ;/
010h 4 Chunk Name "BTAF" (File Allocation Table Block) ;\
014h 4 Chunk Size (including above chunk name) ; File
018h 2 Number of Files ; Allocation
01Ah 2 Reserved (0000h) ; Table
01Ch ... FAT (see below) ;/
... 4 Chunk Name "BTNF" (File Name Table Block) ;\
... 4 Chunk Size (including above chunk name) ; File Name
... ... FNT (see below) ; Table
... .. Padding for 4-byte alignment (FFh-filled, if any) ;/
... 4 Chunk Name "GMIF" (File Image Block) ;\
... 4 Chunk Size (including above chunk name) ; File Data
... ... IMG (File Data) ;/
There are a few NARC archives with crippled header, without “NARC” string (eg. rom:\dwc\utility.bin in Over the Hedge, Downhill Jam, and Tony Hawk’s Skateland).
000h 4 FNT Filename Table Offset (always at 10h)
004h 4 FNT Filename Table Size
008h 4 FAT Allocaton Table Offset (at above Offset+Size+Padding)
00Ch 4 FAT Allocaton Table Size
010h .. FNT Filename Table Data
... .. FAT Allocaton Table Data
... .. IMG File Data
The offsets in FAT are relative to IMG=0 (as if IMG would start at begin of file).
Contains ROM addresses for up to 61440 files (File IDs 0000h and up).
Addr Size Expl.
00h 4 Start address (originated at IMG base) (0=Unused Entry)
04h 4 End address (Start+Len) (0=Unused Entry)
For NitroROM, addresses must be after Secure Area (at 8000h and up).
For NitroARC, addresses can be anywhere in the IMG area (at 0 and up).
Directories are fully defined in FNT area, and do not require FAT entries.
Consists of the FNT Directory Table, followed by one or more FNT Sub-Tables.
To interprete the directory tree: Start at the 1st Main-Table entry, which is referencing to a Sub-Table, any directories in the Sub-Table are referencing to Main-Table entries, which are referencing to further Sub-Tables, and so on.
Consists of a list of up to 4096 directories (Directory IDs F000h and up).
Addr Size Expl.
00h 4 Offset to Sub-table (originated at FNT base)
04h 2 ID of first file in Sub-table (0000h..EFFFh)
For first entry (ID F000h, root directory):
06h 2 Total Number of directories (1..4096)
Further entries (ID F001h..FFFFh, sub-directories):
06h 2 ID of parent directory (F000h..FFFEh)
Contains ASCII names for all files and sub-directories within a directory.
Addr Size Expl.
00h 1 Type/Length
01h..7Fh File Entry (Length=1..127, without ID field)
81h..FFh Sub-Directory Entry (Length=1..127, plus ID field)
00h End of Sub-Table
80h Reserved
01h LEN File or Sub-Directory Name, case-sensitive, without any ending
zero, ASCII 20h..7Eh, except for characters \/?"<>*:;|
Below for Sub-Directory Entries only:
LEN+1 2 Sub-Directory ID (F001h..FFFFh) ;see FNT+(ID AND FFFh)*8
File Entries do not have above ID field. Instead, File IDs are assigned in incrementing order (starting at the “First ID” value specified in the Directory Table).
Somehow related to Nintendo’s compiler, allows to assign compiler Overlay IDs to filesystem File IDs, and to define additional information such like load addresses.
Addr Size Expl.
00h 4 Overlay ID
04h 4 RAM Address ;Point at which to load
08h 4 RAM Size ;Amount to load
0Ch 4 BSS Size ;Size of BSS data region
10h 4 Static initialiser start address
14h 4 Static initialiser end address
18h 4 File ID (0000h..EFFFh)
1Ch 4 Reserved (zero)
The base/size of FAT, FNT, OVT areas is defined in cartridge header,
PassMe is an adapter connected between the DS and an original NDS cartridge, used to boot unencrypted code from a flash cartridge in the GBA slot, it replaces the following entries in the original NDS cartridge header:
Addr Siz Patch
004h 4 E59FF018h ;opcode LDR PC,[027FFE24h] at 27FFE04h
01Fh 1 04h ;set autostart bit
022h 1 01h ;set ARM9 rom offset to nn01nnnnh (above secure area)
024h 4 027FFE04h ;patch ARM9 entry address to endless loop
034h 4 080000C0h ;patch ARM7 entry address in GBA slot
15Eh 2 nnnnh ;adjust header crc16
After having verified the encrypted chip IDs (from the original cartridge), the console thinks that it has successfully loaded a NDS cartridge, and then jumps to the (patched) entrypoints.
Although the original PassMe requires only the entrypoint, PassMe programs should additionally contain one (or both) of the ID values below, allowing firmware patches to identify & start PassMe games without real PassMe hardware.
0A0h GBA-style Title ("DSBooter")
0ACh GBA-style Gamecode ("PASS")
0C0h ARM7 Entrypoint (32bit ARM code)
Of course, that applies only to early homebrew programs, newer games should use normal NDS cartridge headers.
The GBA-slot access rights in the EXMEMCNT register are initially assigned to the ARM7 CPU, so the ARM9 cannot boot from the flashcard, instead it is switched into an endless loop in Main RAM (which contains a copy of the cartridge header at 27FFE00h and up). The ARM7 must thus copy ARM9 code to Main RAM, and then set the ARM9 entry address by writing to [027FFE24h].
Aside from the 17-pin NDS slot, the DS also includes a 32-pin GBA slot. This slot is used for GBA backwards compatibility mode. Additionally, in DS mode, it can be as expansion port, or for importing data from GBA games.
NDS: Normal 32pin slot
DS Lite: Short 32pin slot (GBA cards stick out)
DSi: N/A (dropped support for GBA carts, and for DS-expansions)
In DS mode, ROM, SRAM, FLASH backup, and whatever peripherals contained in older GBA cartridges can be accessed (almost) identically as in GBA mode,
In DS mode, only one ROM-region is present at 8000000h-9FFFFFFh (ie. without the GBA’s mirrored WS1 and WS2 regions at A000000h-DFFFFFFh). The expansion region (for SRAM/FLASH/etc) has been moved from E000000h-E00FFFFh (GBA-mode) to A000000h-A00FFFFh (DS-mode).
GBA timings are specified as “waitstates” (excluding 1 access cycle), NDS timings are specified as (total) “access time”. And, the NDS bus-clock is twice as fast as for GBA. So, for “N” GBA waitstates, the NDS access time should be “(N+1)*2”. Timings are controlled via NDS EXMEMCNT instead GBA WAITCNT,
EEPROMs in GBA carts cannot be accessed in DS mode. The EEPROMs should be accessed with 8 waits on GBA, ie. 18 cycles on NDS on both 1st/2nd access. But, 2nd access is restricted to max 6 cycles in NDS mode, which is ways too fast.
The Rumble Pak comes bundled with Metroid Prime Pinball. It contains a small actuator made by ALPS to make it rumble. The original device (NTR-008) is sized like a normal GBA cartridge, and there’s also shorter variant for the DS-Lite (USG-006).
The rumble pak is pretty simple internally, it only wires up to a few pins on the GBA Cartridge Port:
VCC, GND, /WR, AD1, and IRQ (grounded)
AD1 runs into a little 8 pin chip, which is probably just a latch on the rising edge of /WR. A line runs from this chip to a transistor that is directly connected to the actuator. The only other chip on the board is a 5 pin jobber, probably a power component.
For detection, AD1 seems to be pulled low when reading from it, the other AD lines are open bus (containing the halfword address), so one can do:
for i=0 to 0FFFh
if halfword[8000000h+i*2]<>(i and FFFDh) then <not_a_ds_rumble_pak>
next i
The actuator doesn’t have an on/off setting like a motor, it rumbles when you switch it between the two settings. Switch frequently for a fast rumble, and fairly slowly for more of a ‘tick’ effect. That should be done via timer irq:
rumble_state = rumble_state xor 0002h
halfword[8000000h]=rumble_state
Unknown if one of the two states has higher power-consumption than the other, ie. if it’s a “pull/release” mechanism, if so, then disabling rumble should be done by using the “release” state, which would be AD1=0, or AD1=1…?
Note: The v3 firmware can detect the Rumble Pak as an option pak, but it does not provide an enable/disable rumble option in the alarm menu.
There’s also another DS add-on with rumble. That device uses AD8 (instead AD1) to control rumble, and, it’s using a classic motor (ie. it’s rumbling while and as long as the latched AD8 value is “1”).
There are also a few GBA games that contain built-in Rumble, and which could be used by NDS games as well. To be user friendly, best support both types.
Add-on device for the japanese title Magukiddo. The optical sensor is attached underneath of the console (connected to the GBA slot).
The sensor is an Agilent ADNS-2030 Low Power Optical Mouse Sensor (16pin DIP chip with built-in optical sensor, and external LED light source) with two-wire serial bus (CLK and DTA).
Index (Bit7=Direction; 0=Read, 1=Write):
00h Product_ID (R) (03h)
01h Revision_ID (R) (10h=Rev. 1.0) (20h=Used in DS-option-pak)
02h Motion/Status Flags (R)
03h Delta_X (R) (signed 8bit) (automatically reset to 00h after reading)
04h Delta_Y (R) (signed 8bit) (automatically reset to 00h after reading)
05h SQUAL (R) (surface quality) (unsigned 8bit)
06h Average_Pixel (R) (unsigned 6bit, upper 2bit unused)
07h Maximum_Pixel (R) (unsigned 6bit, upper 2bit unused)
08h Reserved
09h Reserved
0Ah Configuration_bits (R/W)
0Bh Reserved
0Ch Data_Out_Lower (R)
0Dh Data_Out_Upper (R)
0Eh Shutter_Lower (R)
0Fh Shutter_Upper (R)
10h Frame_Period_Lower (R/W)
11h Frame_Period_Upper (R/W)
Motion/Status Flags:
7 Motion since last report or PD (0=None, 1=Motion occurred)
6 Reserved
5 LED Fault detected (0=No fault, 1=Fault detected)
4 Delta Y Overflow (0=No overflow, 1=Overflow occured)
3 Delta X Overflow (0=No overflow, 1=Overflow occured)
2 Reserved
1 Reserved
0 Resolution in counts per inch (0=400, 1=800)
Configuration_bits:
7 Reset Power up defaults (W) (0=No, 1=Reset)
6 LED Shutter Mode (0=LED always on, 1=LED only on when shutter is open)
5 Self Test (W) (0=No, 1=Perform all self tests)
4 Resolution in counts per inch (0=400, 1=800)
3 Dump 16x16 Pixel bitmap (0=No, 1=Dump via Data_Out ports)
2 Reserved
1 Reserved
0 Sleep Mode (0=Normal/Sleep after 1 second, 1=Always awake)
_______
|74273 |
/WR -----------------> |CLK | _____
AD1/SIO CLK ---------> |D1 Q1|--------------> CLK |74125|
AD2 power control ---> |D2 Q2|---> ____ | |
AD3/SIO DIR ---------> |D3 Q3|------o-|7400\________|/EN |
AD8 rumble on/off ---> |D? Q?|---> '-|____/ | |
AD0/SIO DTA ----o----> |D5 Q5|----------------------|A Y|--o--DTA
| |_______| |- - -| |
____ '-------------------------------------|Y A|--'
/RD ---|7400\______ ____ | |
/RD ---|____/ |7400\_____________________________|/EN |
A19 _______________|____/ |_____|
7400 Quad NAND Gate, 74273 8bit Latch
AD0 Optical Sensor Serial Data (0=Low, 1=High)
AD1 Optical Sensor Serial Clock (0=Low, 1=High)
AD2 Optical Sensor Power (0=Off, 1=On)
AD3 Optical Sensor Serial Direction (0=Read, 1=Write)
AD8 Rumble Motor (0=Off, 1=On)
Thanks: Daniel Palmer
There are several RAM expansions for the NDS. The RAM cartridge connects to the GBA slot; can can be then accessed from cartridges in the DS slot.
Opera (8MB RAM) (official RAM expansion for Opera browser)
EZ3/4/3-in-1 (8-16MB RAM, plus FLASH, plus rumble)
Supercard (32MB)
M3 (32MB)
G6 (32MB)
The recommended access time (waitstates) for all memory types is unknown. Unknown which programs do use these expansions for which purposes (aside from the Opera browser).
Thanks to Rick “Lick” Wong for info on detection and unlocking.
base=9000000h, size=800000h (8MB)
unlock=1, lock=0
STRH [8240000h],lock/unlock
base=8400000h, size=VAR (8MB..16MB)
locking/unlocking/detection see below
base=8000000h, size=1FFFFFEh (32MB minus last two bytes?)
unlock=5 (RAM_RW), lock=3 (MEDIA)
STRH [9FFFFFEh],A55Ah
STRH [9FFFFFEh],A55Ah
STRH [9FFFFFEh],lock/unlock
STRH [9FFFFFEh],lock/unlock
base=8000000h, size=2000000h (32MB)
unlock=00400006h, lock=00400003h
LDRH Rd,[8E00002h]
LDRH Rd,[800000Eh]
LDRH Rd,[8801FFCh]
LDRH Rd,[800104Ah]
LDRH Rd,[8800612h]
LDRH Rd,[8000000h]
LDRH Rd,[8801B66h]
LDRH Rd,[8000000h+(lock/unlock)*2]
LDRH Rd,[800080Eh]
LDRH Rd,[8000000h]
LDRH Rd,[80001E4h]
LDRH Rd,[80001E4h]
LDRH Rd,[8000188h]
LDRH Rd,[8000188h]
base=8000000h, size=2000000h (32MB)
unlock=6, lock=3
LDRH Rd,[9000000h]
LDRH Rd,[9FFFFE0h]
LDRH Rd,[9FFFFECh]
LDRH Rd,[9FFFFECh]
LDRH Rd,[9FFFFECh]
LDRH Rd,[9FFFFFCh]
LDRH Rd,[9FFFFFCh]
LDRH Rd,[9FFFFFCh]
LDRH Rd,[9FFFF4Ah]
LDRH Rd,[9FFFF4Ah]
LDRH Rd,[9FFFF4Ah]
LDRH Rd,[9200000h+(lock/unlock)*2]
LDRH Rd,[9FFFFF0h]
LDRH Rd,[9FFFFE8h]
For EZ, detection works as so:
ez_ram_test: ;Based on DSLinux Amadeus' detection
ez_subfunc(9880000h,8000h) ;-SetRompage (OS mode)
ez_subfunc(9C40000h,1500h) ;-OpenNorWrite
[08400000h]=1234h ;\
if [08400000h]=1234h ; test writability at 8400000h
[8000000h]=4321h ; and non-writability at 8000000h
if [8000000h]<>4321h ;
return true ;/
ez_subfunc(9C40000h,D200h) ;CloseNorWrite
ez_subfunc(9880000h,0160h) ;SetRompage (0160h)
ez_subfunc(9C40000h,1500h) ;OpenNorWrite
[8400000h]=1234h ;\
if [8400000h]=1234h ; test writability at 8400000h
return true ;/
return false ;-failed
ez_subfunc(addr,data):
STRH [9FE0000h],D200h
STRH [8000000h],1500h
STRH [8020000h],D200h
STRH [8040000h],1500h
STRH [addr],data
STRH [9FC0000h],1500h
For all other types (everything except EZ), simply verify that you can write (when unlocked), and that you can’t (when locked).
Pedometer with 2-color LED and button. Step counter results can be transferred to NDS via IrDA.
Pedometer with LCD, speaker, and three buttons. The purpose is more or less unknown: Apart from communicating with the NDS, the IrDA can be also used communicate with other P-Walkers (maybe for trading/fighting?). The GUI supports Teams, Routes, Events, Items (maybe for some built-in interactice game engine?). There is no intended way to run custom program code (though it can be tweaked to do so via CPU Memory Write command).
The NDS cartridges and Activity Meter and P-Walker contain Renesas H8/3860X CPUs with H8/300H instruction set and on-chip firmware. In the cartridge it’s merely used for forwading IR messages via SPI bus, in the pedometer it’s handling step sensors, step counting, EEPROM logging, buttons, LED/LCD, RTC/time, IR messages, etc.
H8/300H Series Programming Manual (Hitachi, 257 pages) ;-Opcodes
H8/38602R Group Hardware Manual (Renesas, 554 pages) ;-SFR's
The addition of H8/38606 Group (Renesas, 6 pages) ;-FLASH/ROM/RAM
For P-Walker:
BMA150 Triaxial digital acceleration sensor Data sheet (Bosch, 56 pages)
SSD1854 Advance Information (Solomon System, 48 pages) ;-LCD driver
<a href="http://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker"><font color="#808080">http://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker</font></a>
;-Disassembly/Story
<a href="http://forums.nesdev.com/viewtopic.php?f=23&t=21140#p265388"><font color="#808080">http://forums.nesdev.com/viewtopic.php?f=23&t=21140#p265388</font></a>
;-Forum
There are two NDS cart firmware versions with minor differences:
OLD was used in Walk with Me (maybe also Active Health?)
NEW was used in the P-Letter game series
The IR-port is accessed via SPI bus commands; that bus is also shared for accessing FLASH/EEPROM memory (via 00h-prefix).
04h,04h Initial dummy in walk with me (bugged read or wrdi?)
00h,cmd,params[...] Savedata access
01h,00h,00h Infrared RX (none, len=0, plus dummy data=0)
01h,len,data[len] Infrared RX (OLD: max 84h bytes, NEW: max B8h bytes)
02h,data[...] Infrared TX (OLD: max 84h bytes, NEW: max B8h bytes)
02h,F2h,data[...] OLD: ignored (refuses to TX data starting with F2h)
03h,msb,lsb,data Memory Write 8bit ;\MOV.B
04h,msb,lsb,data Memory Read 8bit ;/
05h,msb,lsb,data,data Memory Write 16bit ;\MOV.W (fails on 8bit SFRs?)
06h,msb,lsb,data,data Memory Read 16bit ;/
07h,00h,num,num,num,... Blah, returns num params from previous spi command
08h..FFh OLD: Ignored (keeps awaiting a SPI command byte)
08h,ver NEW: Returns version (ver=AAh)
09h..FFh OLD: Ignored (returns zeropadding)
The SPI transfers are working at max 1MHz transfer clock, and they do require a delay after each byte:
Waiting 800h clks at 33MHz seems to work okay (eg. MOV r0,200h // SWI 03h on ARM7). The NEW ROM version disables IR polling when doing the SPI transfers for RX/TX data blocks (so it may work with shorter delays between the data[…] bytes).
The savedata access is directly passed to the FLASH/EEPROM chip and does work at 4MHz without delays (except, the leading 00h prefix must be transferred at 1MHz plus delay, and another delay is needed when releasing chipselect after last byte).
Note: The NDS cart slot IRQ pin is GNDed in Walk with Me (ie. there is no IRQ for SPI/IR status).
SPI access does require cart power on and reset (via DSi SCFG registers), but doesn’t require any ROM commands like secure area loading. Confusingly, the ROM Chip ID seems to have an IR flag in bit0 of 3rd byte (although the ROM chip isn’t wired to IR hardware at all).
The IR transfers are using a fixed baudrate: 115200 baud, 8n1, one-directional (RX gets disabled during TX). The RX/TX commands are transferring “packets” (with each “packet” being terminated by a “pause” in the IR transmission; that “packet+pause” mechanism is making it impossible to use streaming for transferring larger blocks that exceed the buffer size of max 84h or B8h bytes).
The RX command will return empty data with len=00h until a WHOLE packet has been received via IR.
The TX command won’t start the IR transfer until the WHOLE packet has been written via SPI, with packet end indicated by releasing chip select.
There is no way to detect TX transfer end (other than computing the expected tranfer time and using an ARM timer).
However, Nintendo is sending a Reply for most TX commands, so one can simply wait for RX packets to determine TX completion (if neither Reply nor Checksum Error are received then one will still need timeout handling).
These commands are normally not used. The memory commands do forcefully abort IR transfers, so they can’t be used for polling IR transfer status.
However, they can be used for dumping the firmware ROM, and they could be used to upload/execute custom code in RAM, which may allow to overcome some of the above IR transfer restrictions (other baudrates, fewer delays, better streaming, not ignoring byte F2h, etc).
NDS/DSi Carts with IR support are having special game code with “I” in first letter (NTR-Ixxx or TWL-Ixxx). There are reportedly pirate/bootleg versions of the P-Letter games without IR hardware, unknown if they do nethertheless have the “I” in the gamecode.
Emulators can detect the leading 00h prefix for Savedata access, although that detection may go wrong if preceeded by IR access. Emulators can additionally detect the slow 1MHz SPI clock used for IR access (and for 00h prefix).
[0FFD6h].0 Port 3 Data bit0 ?
[0FFD6h].2 Port 3 Data bit2 ?
[0FFDBh].3 Port 8 Data bit3 OUT Savedata chipselect (0=select) (cmd 00h)
[0FFDBh].2 Port 8 Data bit2 OUT LED color ;\used in UNUSED functions,
[0FFDBh].3 Port 8 Data bit3 OUT LED color ; in OLD ROM only, and
[0FFDEh].0 Port B Data bit0 IN Button input ;/conflicting with Savedata
IrDA IR Transfers
SPI NDS Console (and cmd 00h forwarding to Savedata)
FB80h 200h undocumented and unused RAM, is R/W in my 38600R (!)
FD80h 2 unused ;-unused
FD82h 2 ir_callback ;\main callbacks for ir/spi polling
FD84h 2 spi_callback ;/
FD86h 2 ir_timestamp ;-last ir access (for timeout)?
FD88h 2 spi_timestamp ;-last spi access (for debug or so)?
FD8Ah 1 initial_blah ;-initial state of Port 8.bit3 (not really used)
FD8Bh 1 ir_rxbuf_wrptr ;-ir_rxbuf_wrptr (for incoming IR data)?
FD8Ch 1 ir_rxbuf_rdptr ;-ir_rxbuf_rdptr (for forwarding to spi)?
FD8Dh 84h spi_rx_buf: ;-spi_rx_buf ;(also ir TX buf)
FE11h 84h infrared_rx_buf: ;-infrared_rx_buf
FE95h 1 spi_index ;-spi_index
FE96h 1 ir_tx_index ;-ir_tx_index (from spi buf to TX infrared)
FE97h 1 ir_timeout_flag ;-ir_timeout_flag (or packet end or so?)
FE98h 2 button_num_changes ;\
FE9Ah 2 button_num_pushes ; used only in
FE9Ch 1 button_new_state ; UNUSED functions
FE9Dh 1 button_old_state ;
FE9Eh 1 button_newly_pushed ;
FE9Fh 1 button_offhold ;/
FEA0h E0h stack_area (stacktop at FF80h)
FB80h 200h undocumented and unused RAM, is R/W in my 38600R (!)
FD80h 2 unused ;-unused
FD82h 2 ir_callback ;\main callbacks for ir/spi polling
FD84h 2 spi_callback ;/
FD86h 2 ir_timestamp ;-last ir access (for timeout)?
FD88h 1 ir_rxbuf_wrptr ;-ir_rxbuf_wrptr (for incoming IR data)?
FD89h 1 ir_rxbuf_rdptr ;-blah, always set to 0, never used
FD8Ah 1 spi_index ;-spi_index
FD8Bh 1 ir_tx_index ;-ir_tx_index (from spi buf to TX infrared)
FD8Ch B8h spi_rx_buf ;-spi_rx_buf ;(also ir TX buf)
FE44h B8h+1 infrared_rx_buf ;-infrared_rx_buf (plus space for appending 00h)
FEFDh 1 ir_timeout_flag ;-ir_timeout_flag (or packet end or so?)
FEFEh 82h stack_area (stacktop at FF80h)
Nintendo wants all IR packet bytes to be “encrypted” (XORed by AAh), that encryption/decryption must be done on ARM side. The checksums are calculated as so (on decrypted packets):
sum=0, packet[2,3]=00h,00h ;-initial chksum
for i=0 to size-1
if (i and 1)=0 then sum=sum+packet[i]*100h ;\add in big-endian fashion
if (i and 1)=1 then sum=sum+packet[i] ;/
sum=(sum/10000h)+(sum AND FFFFh) ;\final adjust
sum=(sum/10000h)+(sum) ;/
packet[2,3]=sum,sum/100h ;-store in little-endian
The packets are transferred at 115200 baud, 8n1. End of Packet is indicated by a pause in the IR transmission (that does also indicate the packet size).
Before sending a command packet, one should always wait for incoming data from the Activity Meter (ie. for the FCh byte, or for Reply/ChecksumError responses for previous command).
08,xx,cc,cc,msb,lsb,data[..] CPU Memory Write (len=3Eh max) ;Reply=08
0A,xx,cc,cc,msb,lsb,len CPU Memory Read (len=40h max) ;Reply=0A
0A,xx,cc,cc,FB,9C,len CPU Memory Read FB9Ch with ClrFlag ;Reply=0A
20,xx,cc,cc,msb,lsb,data[..] Serial EEPROM Write (len=3Eh max) ;Reply=20
22,xx,cc,cc,msb,lsb,len Serial EEPROM Read (len=40h max) ;Reply=22
24,00,cc,cc,ss,ss,ss,ss Update Ringbuf_mm ;\ ;Reply=24
24,01,cc,cc,ss,ss,ss,ss Update Ringbuf_hh ; and set ;Reply=24
24,02,cc,cc,ss,ss,ss,ss Update Ringbuf_dd ; 32bit ;Reply=24
24,03,cc,cc,ss,mm,hh Set RTC hh:mm:ss ; seconds ;Reply=24
24,04,cc,cc,ss,ss,ss,ss Raw Set ssssssss ? ;/ ;Reply=24
24,xx,cc,cc,ss,ss,ss,ss Invalid (same as 24,04) ;Reply=24
26,xx,cc,cc Deadlock ;\both same (maybe ;Reply=26
28,xx,cc,cc Deadlock ;/Watchdog/reboot?) ;Reply=26
2A,xx,cc,cc,00,nn Stepback Ringbuf_hh ;\go back nn ;Reply=2A
2A,xx,cc,cc,01,nn Stepback Ringbuf_mm ; entries, ;Reply=2A
2A,xx,cc,cc,02,nn Stepback Ringbuf_dd ;/see [FCDAh] ;Reply=2A
2A,xx,cc,cc,xx,.. Invalid ;Reply=2A
2C,cs,cc,cc Toggle one LED on/off ;Reply=2C
F4,xx,cc,cc Disconnect ;Reply=None
F6,xx,cc,cc Force "Bad Chksum" reply ;Reply=FC
FA,xx,cc,cc Connect ;Reply=F8
FE,... Noise ;\ignored, noise ;Reply=None
FF,... Noise ;/ ;Reply=None
xx,xx,cc,cc Invalid ;-ignored, invalid cmd ;Reply=None
xx,xx,xx,xx Bad Chksum ;Reply=FC
08,sq,cc,cc Reply to Cmd 08 (CPU Memory Write reply)
0A,sq,cc,cc,data[..] Reply to Cmd 0A (CPU Memory Read reply)
20,sq,cc,cc Reply to Cmd 20 (Serial EEPROM Write reply)
22,sq,cc,cc,data[..] Reply to Cmd 22 (Serial EEPROM Read reply)
26,xx,cc,cc Reply to Cmd 26 and 28 (Deadlock reply)
24,xx,cc,cc Reply to Cmd 24 (Update, or Set RTC time)
2A,xx,cc,cc Reply to Cmd 2A (Stepback, with result at [FCDAh])
2C,cs,cc,cc Reply to Cmd 2C (LED reply)
80,FF,cc,cc Factory Reset and Hardware Test completed (or failed)
F8,00,cc,cc Reply to Cmd FA (Connect reply)
FC,xx,cc,cc Reply to Cmd's with Bad Chksum (and Cmd F6)
FC Advertising Msg (after pressing button) (single byte)
cc,cc Checksum (LITTLE-ENDIAN)
msb,lsb Memory Address (big-endian)
ss,ss,ss,ss Seconds since 2001 (big-endian)
ss,mm,hh RTC time HH:MM:SS (BCD) (caution: smashes seconds since 2001)
sq Increasing sequence number in Memory Access replies
cs LED color/state (c=color red/green, s=state on/off)
xx Whatever (don't care?)
There aren’t any specific commands for reading things like step counters, one must instead use the Memory Read/Write commands with hardcoded RAM or EEPROM address, see:
The infrared range (distance/angle) is unknown. Dumping the whole 64K CPU memory space worked without checksum errors at about 5-10cm distance (and that worked without even using the Connect command).
The most important RAM locations are FCE8h=Total steps, and FB9Ch=Unique ID (when using multiple Activity Meters), FCF0h=Daily Goal (to change LED color after N steps). Nintendo is reading/writing a few more RAM locations. And, there are ring buffers with steps per minute/hour/day in EEPROM.
[0FFD4h].0 Port 1 Data bit0 IN Factory Test (0=Test, 1=Normal)
[0FFD4h].2 Port 1 Data bit2 OUT Set for sum of eight A/D conversions
[0FFD6h].0 Port 3 Data bit0 ?
[0FFD6h].2 Port 3 Data bit2 ?
[0FFDBh].2 Port 8 Data bit2 OUT LED color?
[0FFDBh].3 Port 8 Data bit3 OUT LED color?
[0FFDCh].0 Port 9 Data bit0 OUT SPI EEPROM chipselect (0=select)
[0FFDEh].0 Port B Data bit0 IN Button input
IrDA IR Transfers
SPI SPI 8Kbyte EEPROM
A/D Used to read two single-axis sensors (for step counting)?
A/D Also used to read sum of eight A/D conversions (for wakeup from sleep)?
The SPI EEPROM uses same commands as NDS cart savedata:
FB80h 1 Button flags (bit7=curr.state, bit6=newly.pressed, bit5=old.state)
FB81h 1 ... cleared if memread src was unique_id (and other cases)
FB82h 1 ... sys/power mode ?
FB83h 1 ... adc_mode, or power_saving?
FB84h 1 ... clock change request
FB85h 1 ... led_extra_mask (never CLEARED, except on boot, or maybe via IR)
FB86h 1 adc_array_index (index in ADC array X/Y, wraps in range 00h..3Fh)
FB87h 1 ... entrysize of current data in ringbuf (per newest TAG) or so?
FB88h 1 SPI overrun error (probably nonsense, SPI clk can't outrun itself)
FB89h 1 Unused
FB8Ah 1 num_steps_curr_minute (00h..FCh) (no conflict with tag FDh,FEh,FFh)
FB8Bh 1 rtc_event_flags (bit0=minute, bit1=hour, bit2=day, bit3=also.hour)
FB8Ch 1 ... timing offhold for various stuff
FB8Dh 1 some_shift_amount ;READ via IR
FB8Eh 1 Daily goal reached flag (aka LED color) (bit0=reached, bit1=???)
FB8Fh 1 ... timing for LED step pulses?
FB90h 1 ... timing for LED step pulses?
FB91h 1 ... flag for LED step pulse state?
FB92h 1 Hour when new day starts (BCD, usually/always 03h) ;READ via IR
FB93h 1 ... some flag for inactivity low-power mode ?
FB94h 1 LED animation number (1..5, or 0=none) (factory test result)
FB95h 1 Unused
FB96h 1 New day flag
FB97h 1 Fixed LED mask (this is a "fixed" setting from EEPROM)
FB98h 1 Compare_ctrl_0 ;\for "Compare Control" HW registers (89h,89h)
FB99h 1 Compare_ctrl_1 ;/
FB9Ah 1 New Goal flag (apply [FCF4h] as new goal, starting on next day?)
FB9Bh 1 Unused
FB9Ch 28h Unique ID ;READ via IR (initally set by NDS via RAM+EEPROM writes?)
FBC4h 2 adc_current_x
FBC6h 2 adc_current_y
FBC8h 80h adc_array_x (40h x 16bit)
FC48h 80h adc_array_y (40h x 16bit)
FCC8h 2 adc_scale_factor_x ;\scale factors
FCCAh 2 adc_scale_factor_y ;/
FCCCh 2 adc_scale_unused_z ;\semi-unused (written, but never read)
FCCEh 2 adc_scale_unused_t ;/
FCD0h 2 ringbuf_mm_index (0020h..16A0h) ;READ via IR
FCD2h 2 ringbuf_hh_index (16A1h..1C42h) ;READ via IR
FBD4h 2 Unused
FCD6h 2 ringbuf_dd_index (1C43h..1CDEh) ;READ via IR
FCD8h 2 num_steps_curr_hour (16bit step counter for current hour)
FCDAh 2 ringbuf_stepback_index (result from cmd_2Ah, to be read by cmd_0Ah)
FCDCh 2 adc_inactivity_timer (time since last pedometer step)
FCDEh 2 SPI overrun error counter (related to flag at FB88h)
FCE0h 2 Unused
FCE2h 2 adc_current_sum (sum of eight A/D conversions)
FCE4h 4 seconds_counter (seconds since 1st Jan 2001?, initially 0D2B0B80h)
FCE8h 4 num_steps_lifelong (lifelong TOTAL steps)
FCECh 4 num_steps_today (step counter, for current day)
FCF0h 4 Daily_goal (WRITTEN via IR, NDS cart default=3000 decimal)
FCF4h 4 new_goal_steps (somewhat reload value for daily goal?)
FCF8h 18h Unused
FD10h 2 main_callback (main_adc_button_callback, or ir_callback)
FD12h 40h ir_tx_data (buffer for Memory & EEPROM reads)
FD52h 2 clk_callback (clk_whatever_callback, or 0=none)
FD54h 2 ir_callback (ir_active_callback, or ir_dummy_callback)
FD56h 2 ir_timestamp_last_byte (for sensing SHORT GAPs, aka end-of-packet)
FD58h 2 RX chksum from hdr[2..3]
FD5Ah 2 RX chksum from calculation
FD5Ch 2 ir_timestamp_last_xfer (for sensing LONG GAPs, aka sleep mode)
FD5Eh 1 Unused
FD5Fh 1 ir_rx_len
FD60h 1 ... semi-unused (set to 00h?) (but never read)
FD61h 44h ir_rxtx_buf, hdr[4]+data[40h]
FDA5h 1 ir_tx_hdr_len ;\memorized TX len+hdr[4]
FDA6h 4 ir_tx_hdr_copy ;/(never actually used)
FDAAh 1 bad_chksum_count, give up sending bad_chksum replies after 3 errors
FDABh 1 bad_chksum_flag, request reply_FCh (bad_chksum)
FDACh 80h ... array (40h x 16bit) ;\
FE2Ch 80h ... array (40h x 16bit) ; analog sine/cosine
FEACh 4 ... dword ; stuff for converting
FEB0h 4 ... dword ; adc to step counter?
FEB4h 1 ... byte ;
FEB5h 1 ... byte ;/
FEB6h 2 Incremented in main_adc_button_callback (but not used elsewhere)
FEB8h 1 Unused ;\maybe meant to be 4-byte tx hdr,
FEB9h 1 TX sequence number ; but only hdr[1] used (as increasing
FEBAh 2 Unused ;/seq.no for memory read/write replies)
FEBCh 4 ... array (2 x 16bit)
FEC0h 4 ... array (2 x 16bit)
FEC4h 4 ... array (2 x 16bit)
FEC8h 4 ... array (2 x 16bit)
FECEh B2h CPU Stack area, initial SP=FF80h
EEPROM:0000h 9 ID "nintendo",00h (9 bytes)
EEPROM:0009h 17h Unused (FFh-filled)
EEPROM:0020h 1681h Ringbuf_mm ;steps per MINUTE for 4 days ;(24*60*4-1)*8bit
EEPROM:16A1h 5A2h Ringbuf_hh ;steps per HOUR for 30 days ;(24*30+1)*16bit
EEPROM:1C43h 9Ch Ringbuf_dd ;steps per DAY for 52 days ;(52)*24bit
EEPROM:1CDFh 1 Unused (FFh) (padding ringbuf's to 20h-byte-boundary)
EEPROM:1CE0h 200h Unused (FFh-filled)
EEPROM:1EE0h 8+1 ADC_scale_values (4x16bit) ;RAM:FCC8h ;\
EEPROM:1EE9h 2+1 ADC sum_limit ;RAM:stack ; these EEPROM
EEPROM:1EECh 3 Unused ; settings
EEPROM:1EEFh 4+1 Num_steps_lifelong ;RAM:FCE8h ; have 1-byte
EEPROM:1EF4h 1+1 Fixed LED Mask ;RAM:FB97h ; checksums
EEPROM:1EF6h 2 Unused ; appended, and
EEPROM:1EF8h 1+1 Some_shift_amount ;RAM:FB8Dh ; backups at
EEPROM:1EFAh 4+1 Daily_goal ;RAM:FCF0h ; 1F40h-1F9Fh
EEPROM:1EFFh 4+1 New_goal_steps ;RAM:FCF4h ;
EEPROM:1F04h 28h+1 Unique ID ;RAM:FB9Ch ;
EEPROM:1F2Dh 13h Unused (00h-filled) ;/
EEPROM:1F40h 60h Backup copies of above data at 1EE0h..1F3Fh ;-backups
EEPROM:1FA0h 2 Error code (initially FFFFh)
EEPROM:1FA2h 1 Reboot counter (initially 00h or 01h ?)
EEPROM:1FA3h 5Dh Unused (FFh-filled)
The MM/HH/DD ring buffers are steps per minute/hour/day accordingly, mixed with special tags:
00xxh Zero steps for N minutes (N=max FCh) ;\in ringbuf_mm
xxh N steps per minute (N=01h..FCh) ;/
xxxxh N steps per hour (N=0000h..FFFFh) ;-in ringbuf_hh
xxxxxxh N steps per day (N=000000h..FFFFFFh) ;-in ringbuf_dd
FDxxxxxxxxxxh Timestamp, reversed-BCD-digit-order, seconds since 2001 or so?
FEh Newest entry marker?
FFh Unused entry marker?
The firmware contains code for searching tags FDh/FEh/FFh forwards and backwards, that works well with BCD values and 8bit counters, but unknown it can work with the 16bit/24bit counters. The BCD digits are stored backwards (eg. 12345 = FD5432100000h). Rinbuf pointers can be read from RAM locations FCD0h, FCD2h, FCD6h, and FCDAh).
Commands are usually send from NDS (or from other Walkers)
EEPROM Commands (Cmd 02,04,0C,0E,82) ;\From NDS or Walker
Connect Commands (Cmd F8,FA,FC) ;/
Peer Commands (Cmd 10...1C) ;-From Walker
Unused Commands (Cmd's with * marking) ;-From Prototype tests?
Other Commands (Cmd's other than above) ;-From NDS
00,hi,..,lzss(..) EEPROM Write [hi00h..hi7Fh] Compressed ;Reply=04
80,hi,..,lzss(..) EEPROM Write [hi80h..hiFFh] Compressed ;Reply=04
02,hi,..,data(..) EEPROM Write [hi00h..hi7Fh] Raw ;Reply=04
82,hi,..,data(..) EEPROM Write [hi80h..hiFFh] Raw ;Reply=04
04,xx,.. EEPROM Write Reply ;SendMoreCmd(s)
06,hi,..,lo,data(nn)* CPU Memory Write [hilo+(0..nn-1)] ;Reply=06
0A,hi,..,lo,data(nn)* EEPROM Write Random Len [hilo+(0..nn-1)] ;Reply=04
0C,xx,..,hi,lo,nn EEPROM Read Request [hilo+(0..nn-1)] ;Reply=0E
0E,xx,..,data(nn) EEPROM Read Reply ;SendMoreCmd(s)
10,xx,..,data(68h) Peer Step 1 Request ;Reply=12
12,xx,..,data(68h) Peer Step 1 Reply ;SendMoreCmd(s)
14,xx,..,data(38h) Peer Step 2 Request ;Reply=16
16,xx,.. Peer Step 2 Reply ;Reply=16 or None
1C,xx,.. Peer Refuse ;Reply=None+Disconnect
20,xx,.. Identity Read Request ;Reply=22
24,xx,.. * Ping Request ;Reply=26
2A,xx,..,none? Unique ID Read Request ;Reply=2A
2C,xx,..,none? * Unique ID Read Request slightly other ;Reply=2A
32,xx,..,data(28h?) * Identity Write Request 1 ;Reply=34
36,xx,.. * Connection Error 1 ;Reply=None
38,xx,.. * Walk Start Silent ;Reply=38
40,xx,..,data(28h?) * Identity Write Request 2 ;Reply=42
44,xx,.. * Connection Error 2 ;Reply=None
4E,xx,.. Walk End Request ;Reply=50
52,xx,..,data(28h?)?? Identity Write Request 3 ;Reply=54
56,xx,.. * Connection Error 3 ;Reply=None
5A,xx,.. Walk Start Nonsilent ;Reply=5A
60,xx,..,data(28h?) * Identity Write Request 4 ;Reply=62
64,xx,.. * Connection Error 4 ;Reply=None
66,xx,.. * Walk End Request OTHER ;Reply=68
9C,xx,.. * Error Whatever ;Reply=9C+Disconnect
9E,xx,.. * Error Weird Participate ;Reply=9E+Disconnect
A0,xx,.. * Weird Participate 1 ;Reply=A0 or 9E
A2,xx,.. * Weird Participate 2 ;Reply=A2 or 9E
A4,xx,.. * Weird Participate 3 ;Reply=A4 or 9E
A6,xx,.. * Weird Participate 4 ;Reply=A6 or 9E
A8,xx,.. * Weird Participate 5 ;Reply=A8 or 9E
AA,xx,.. * Weird Participate 6 ;Reply=AA or 9E
AC,xx,.. * Weird Participate 7 ;Reply=AC or 9E
AE,xx,.. * Weird Participate 8 ;Reply=AE or 9E
B8,xx,.. * Award Stamp Heart ;Reply=D8
BA,xx,.. * Award Stamp Spade ;Reply=DA
BC,xx,.. * Award Stamp Diamond ;Reply=DC
BE,xx,.. * Award Stamp Club ;Reply=DE
C0,xx,.. * Award Special Map ;Reply=C0
C2,xx,.. * Award Event P-Letter ;Reply=C2
C4,xx,.. * Award Event Item ;Reply=C4
C6,xx,.. * Award Event Route ;Reply=C6
D0,xx,.. * Award All Stamps and Special Map ;Reply=C0
D2,xx,.. * Award All Stamps and Event P-Letter ;Reply=C2
D4,xx,.. * Award All Stamps and Event Item ;Reply=C4
D6,xx,.. * Award All Stamps and Event Route ;Reply=C6
D8,xx,.. * Connection Error 5 ;Reply=None
F0,xx,..,data(71h) ?? Enroll Data (28h+40h+8+1 bytes) ;Reply=F0
F4,xx,.. * Disconnect ;Reply=None+Disconnect
F8,02,.. Connection Reply from Walker ;SendCmd=1002
FA,01,.. Connection Request from NDS ;Reply=F802
FA,02,.. Connection Request from Walker ;Reply=F802
FA,xx,.. Connection Request invalid ;Reply=None+Disconnect
FC Connection Beacon from Walker ;SendCmd=FA
FE,01,..,data(8) * EEPROM Write [0008h..000Fh] ;Reply=FE
xx * Ignored (single byte other than FC) ;Reply=None
xx,xx,.. * Invalid Cmd ;Reply=None
xx,xx,xxxx * Ignored (wrong 4-byte ID for Cmd 00-F7) ;Reply=None
xx,xx,xxxx * Bad Checksum (disconnect if too often) ;Reply=None
02,hi,..,data(nn) EEPROM Write ... ;Cmd=Peer
82,hi,..,data(nn) EEPROM Write ... ;Cmd=Peer
04,hi,.. EEPROM Write Reply ;Cmd=00/02/0A/80/82
06,hi,.. * CPU Memory Write Reply ;Cmd=06h
0C,02,..,hi,lo,nn EEPROM Read Request ;Peer, EEPROM Read ;Cmd=0Eh
0E,02,..,data(nn) EEPROM Read Reply ;Cmd=0Ch
10,02,..,data(68h) Peer Step 1 Request (after Connect Reply);Cmd=F8h
12,02,..,data(68h) Peer Step 1 Reply ;Cmd=10h
14,02,..,data(38h) Peer Step 2 Request ;Cmd=0Eh
16,02,.. Peer Step 2 Reply ;Cmd=14h/16h
1C,02,.. Peer Refuse ;Cmd=10h/12h
22,02,..,data(68h) Identitiy Read Reply ;Cmd=20h
26,02,.. * Ping Reply ;Cmd=24h
2A,02,..,data(28h) Unique ID Reply ;Cmd=2Ah/2Ch
34,02,.. * Identitiy Write 1 Reply ;Cmd=32h
38,02,.. * Walk Start silent Reply ;Cmd=38h
42,02,.. * Identitiy Write 2 Reply ;Cmd=40h
50,02,.. Walk End Reply ;Cmd=4Eh
54,02,.. ?? Identitiy Write 3 Reply ;Cmd=52h
5A,02,.. Walk Start nonsilent Reply ;Cmd=5Ah
62,02,.. * Identitiy Write 4 Reply ;Cmd=60h
68,02,.. * Walk End OTHER Reply ;Cmd=66h
9C,02,.. * Weird Whatever Reply-to-Reply? ;Cmd=9Ch
9E,02,..,data(11h) * Weird Participated Reply ;Cmd=A0h..AEh
9E,02,.. * Weird Participated Reply-to-Reply? ;Cmd=9Eh
A0,02,..,data(11h) * Weird Participated Reply 1 ;Cmd=A0h
A2,02,..,data(11h) * Weird Participated Reply 2 ;Cmd=A2h
A4,02,..,data(11h) * Weird Participated Reply 3 ;Cmd=A4h
A6,02,..,data(11h) * Weird Participated Reply 4 ;Cmd=A6h
A8,02,..,data(11h) * Weird Participated Reply 5 ;Cmd=A8h
AA,02,..,data(11h) * Weird Participated Reply 6 ;Cmd=AAh
AC,02,..,data(11h) * Weird Participated Reply 7 ;Cmd=ACh
AE,02,..,data(11h) * Weird Participated Reply 8 ;Cmd=AEh
C0,02,.. * Award Special Map Reply ;Cmd=C0h/D0h
C2,02,.. * Award Event P-Letter Reply ;Cmd=C2h/D2h
C4,02,.. * Award Event Item Reply ;Cmd=C4h/D4h
C6,02,.. * Award Event Route Reply ;Cmd=C6h/D6h
C8,02,.. * Award Stamp Heart Reply ;Cmd=B8h
CA,02,.. * Award Stamp Spade Reply ;Cmd=BAh
CC,02,.. * Award Stamp Diamond Reply ;Cmd=BCh
CE,02,.. * Award Stamp Club Reply ;Cmd=BEh
F0,02,..,data(28h) ?? Enroll Reply ;Cmd=F0h
F8,02,.. Connect Reply ;Cmd=FAh
FA,02,.. Connect Request from walker ;Cmd=FCh
FC Connection Beacon ;Button?
FE,02,.. * EEPROM Write [0008h..000Fh] Reply ;Cmd=FEh
-?- Checksum Error... has no reply? or maybe sends Beacons?
.. short for 16bit Checksum at hdr[2..3] and 32bit Session ID at hdr[4..7]
xx somewhat don't care (usually 01h=From NDS, or 02h=From Walker)
All 16bit checksum and the IR “encryption” (XOR by AAh), seem to be same as for Activity Meter (see there).
The Connect Request+Reply commands are sending a “random” Session ID, all following commands (except F8h and up) must use those two random values XORed together as Session ID.
The Compressed EEPROM commands are writing decompressed 80h-bytes of data to EEPROM (ie. the compression is only used to speedup the IR transfer, not to save memory). The compression format is Nintendo’s standard LZSS (include the header value 10h, and little-endian 24bit length; which should be always 80h). Special case: Compressed writes with exactly 80h bytes of incoming data are treated as uncompressed writes (to be used when compression ratio is worse than no compression).
[0FFD4h].0 Port 1 Data bit0 OUT SPI LCD writeselect (0=select)
[0FFD4h].1 Port 1 Data bit1 OUT SPI LCD access mode (0=Cmd, 1=Dta?)
[0FFD4h].2 Port 1 Data bit2 OUT SPI EEPROM chipselect (0=select)
[0FFD6h].0 Port 3 Data bit0 ?
[0FFD6h].2 Port 3 Data bit2 ?
[0FFDBh].2 Port 8 Data bit2 ?
[0FFDBh].3 Port 8 Data bit3 ?
[0FFDBh].4 Port 8 Data bit4 OUT A/D related ... whatfor LCD? accel? batt?
[0FFDCh].0 Port 9 Data bit0 OUT SPI Accelerometer chipselect (0=select)
[0FFDEh].0 Port B Data bit0 IN ? ;\
[0FFDEh].2 Port B Data bit2 IN ? ; maybe buttons
[0FFDEh].4 Port B Data bit4 IN ? ;/
[0FFDEh].5 Port B Data bit5 OUT ?
Timer W General A/B/C Audio Frequency/Volume
IrDA IR Transfers
SPI SPI 64Kbyte EEPROM, LCD Cmd/Data, Accelerometer
A/D whatfor LCD? accel? batt?
The SPI EEPROM uses same commands as NDS cart savedata:
DS Cartridge Backup For the other two SPI-like devices, see:
F780h 60h Misc variables
F7E0h 2 main_callback ;<--
F7E2h ECh Misc variables
F8CEh 8+80h Infrared RX/TX buffer hdr+data (also misc/heap)
F956h 62Ah Temp buffer, free RAM, and stack ;<--
FF80h - Stacktop (end of RAM)
For some quick hacks, Dmitry recommends these ROM addresses (which won’t work when reflashing the firmware).
0772h Send IR packet (F8D6h=src, r0l=len, r0h=hdr[0], r1l=hdr[1])
08D6h Default callback (when in IR transfer mode)
259Eh Watchdog refresh
The EEPROM contains some important basic data, plus GUI related bitmaps (mostly text strings pre-rendered as bitmaps for the local user name & game language) (and maybe(?) also game specific customizations).
0000h 8 ID "nintendo" (set after initial power-up eeprom init)
0008h 8 ID whatever (set via Cmd F0h and FEh) (never read)
0010h 62h ???
0072h 1 Number of watchdog resets
0073h 0Dh ???
0080h 02h+1 ADC calibration (factory-provided) ;\
0083h 28h+1 Unique ID (set via Cmd F0h) ; with 1-byte
00ACh 40h+1 LCD ConfigCmds (set via Cmd F0h) ; checksums
00EDh 68h+1 Identity Data ("provisioned" at walk start time) ; and backup
0156h 18h+1 Health Data ("provisioned" at walk start time) ; copies at
016Fh 01h+1 Copy Flag (00h=Normal, A5h=copy was interrupted) ; 0180h-027Fh
0171h 0Fh Unused ;/
0180h 100h Backup copies of entries at 0080h-0017Fh
0280h ... Various Bitmaps
8C70h ... Various Garbage, Bitmaps, Items, Team, Route
CE8Ah 2 current watts written to eeprom by cmd 20h before replying
(likely so remote can read them directly). u16 BE
CE8Ch ... Various stuff
CEF0h 1Ch Historic step count per day. u32 each, BE,
[0] is yesterday, [1] is day before, etc...
CF0Ch ... Various stuff
__________________ Data Structures (in EEPROM and Packets) ___________________
00h 28h Generated by the DS game at pairing time, unique per walker
All multi-byte values LE, unless otherwise specified
00h 4 Unknown (LE, always 1?) ;\written from game packet at walk start
04h 4 Unknown (LE, always 1?) ; ;<-- 0 at walk end ;copied from [0]
08h 2 Unknown (LE, always 7?) ;
0Ah 2 Unknown (LE, always 7?) ;/ ;<-- 0 at walk end ;copied from [8?]
0Ch 2 TrainerTID
0Eh 2 TrainerSID
10h 28h Unique ID
38h 10h EventBitmap (aka bitfield with 128 event flags?)
48h 10h Trainer Name (8 chars, UTF-16)
58h 1 Unknown
59h 1 Unknown
5Ah 1 Unknown
5Bh 1 Flags (bit0=PairedToGame, bit1=HasPoke, bit2=PokeJoinedOnAWalk)
5Ch 1 ProtoVer (02h) (written by DS, refuse peer's with other values)
5Dh 1 Unknown
5Eh 1 ProtoSubver (00h) (written by DS, refuse peer's with other values)
5Fh 1 Unknown (02h) (written by DS at walk start)
60h 4 LastSyncTime ;Big Endian ;in WHAT... maybe seconds since WHEN?
64h 4 StepCount ;Big Endian ;since WHEN... today? lifetime? lastsync?
All multi-byte values LE, unless otherwise specified
00h 4 curStepCount (since WHEN?)
04h 2 curWatts
06h 2 Unused
08h 4 Unknown, copied from IdentityData[00h]
0Ch 2 Unknown, copied from IdentityData[08h]
0Eh 2 Species
10h 16h P-Nickname (11 chars, UTF-16) ;\the actual names in bitmap format
26h 10h Trainer Name (8 chars, UTF-16) ;/are stored elsewhere in EEPROM?
36h 1 GenderForm
37h 1 HasSpecialForms (spinda, arceus, unown, etc.)
Stored reliably at 00ACh/01ACh.
00h 1 u8 contrastAndFlags (if 00h/FFh? commands at ROM:BEB8h will be used)
01h 3Fh u8 commands[3fh] (Commands, or FDh,NNh=Delay(NNh), FEh=End of list)
00h 28h Unique ID ;always written
28h 40h LCD Config Data ;written or verified depending on byte[70h]
68h 8 Whatever ID ;always written to EEPROM:0008h
70h 1 LCD Action (00h=WriteA, 01h=Compare, 03h=WriteB, 02h/04h-FFh=Nop)
Stored reliably at 0156h/0256h. Cached in RAM at F780h.
Big Endian unless otherwise noted.
00h 4 u32 lifetimeTotalSteps
04h 4 u32 todaySteps //zeroed at midnight
08h 4 u32 lastSyncTime
0Ch 2 u16 totalDays
0Eh 2 u16 curWatts
10h 2 u16 unk_0
12h 1 u8 unk_1
13h 1 u8 unk_2
14h 3 u8 padding[3]
17h 1 u8 settings (bit0=isOnSpecialRoute, bit1-2=Volume, bit3-6=Contrast)
See Dmitry’s webpage for more “game-specifc” data structures and memory addresses.
1st Byte 2nd Byte Description
00h+(0..Fh) - Set Column Address bit0-3
10h+(0..7) - Set Column Address bit4-6
18h+(0..1) - Set Master/Slave Mode (0=Master, 1=Slave)
1Ah+(2..5) - Reserved
20h+(0..7) - Set Internal Regulator Resistor Ratio
(0..7 = 3.2, 3.9, 4.6, 5.3, 6.0, 6.7, 7.4, 8.1)
28h+(0..7) - Set Power Control Register
bit0: Internal Voltage Booster (0=Off, 1=On)
bit1: Internal Regulator (0=Off, 1=On)
bit2: Output Op-amp Buffer (0=Off, 1=On)
30h+(0..0Fh) - Reserved
40h 00h-xxh Set Display Start Line (0..(WindowRowNumber+15))
41h+(0..2) 00h-xxh Same as above?
44h 00h-xxh Set Display Offset (0..159?) (COM0=ROW0..159)
45h+(0..2) 00h-xxh Same as above?
48h 00h-xxh Set Multiplex Ratio (num lines, duties "1/(16..160)")
49h+(0..2) 00h-xxh Same as above?
4Ch 00h-3Fh Set N-line Inversion (0=Off, 1..63=Reduce crosstalk?)
4Ch 40h-FFh Same as above?
4Dh+(0..2) 00h-FFh Same as above?
50h+(0..7) - Set LCD Bias, corresponding to different mux number
(0=32mux, 2=96mux, 4=128mux, 6=160mux, other=Reserved)
58h+(0..7) - Reserved
60h 00h-FFh Set Upper Window Corner ax (first col of scroll window)
61h 00h-FFh Set Upper Window Corner ay (first row of scroll window)
62h 00h-FFh Set Lower Window Corner bx (last col of scroll window)
63h 00h-FFh Set Lower Window Corner by (last row of scroll window)
64h+(0..3) - Set DC-DC Converter Factor (0=3x, 1=4x, 2/3=5x)
68h+(0..8) - Reserved
81h 00h-3Fh Set Contrast (0..3Fh, 3Fh=Darkest)
82h+(0..5) - Reserved
88h 00h-FFh Set White Mode (bit0-3=FrameA, bit4-7=FrameB)
89h 00h-FFh Set White Mode (bit0-3=FrameC, bit4-7=FrameD)
8Ah 00h-FFh Set Light Gray Mode (bit0-3=FrameA, bit4-7=FrameB)
8Bh 00h-FFh Set Light Gray Mode (bit0-3=FrameC, bit4-7=FrameD)
8Ch 00h-FFh Set Dark Gray Mode (bit0-3=FrameA, bit4-7=FrameB)
8Dh 00h-FFh Set Dark Gray Mode (bit0-3=FrameC, bit4-7=FrameD)
8Eh 00h-FFh Set Black Mode (bit0-3=FrameA, bit4-7=FrameB)
8Fh 00h-FFh Set Black Mode (bit0-3=FrameC, bit4-7=FrameD)
Grey palette programming. These are two-byte commands used to specify
the contrast levels for the gray scale, 4 levels available. The
relationship between gray mode and data in RAM is as follow:
Memory Content
1st Byte 2nd Byte Gray Mode
0 0 White
0 1 Light gray
1 0 Dark gray
1 1 Black
90h+(0..7) - Set PWM and FRC for gray-scale operation
bit0-1: Levels (0/1=Nine, 2=Twelve, 3=Fifteen Levels)
bit2: Frames (0=Four, 1=Three Frames)
Note: Nintendo uses "9 levels" ranging from "0 to 9"
(maybe level 0 is treated as off, thus not counted)
98h+(0..7) - Reserved
A0h+(0..1) - Set Segment Remap (0=Col00h is SEG0, 1=Col7Fh is SEG0)
A2h+(0..1) - Reserved
A4h+(0..1) - Set Entire Display On/Off (0=Show RAM, 1=All Pixels On)
A6h+(0..1) - Set Reverse Display (0=Normal, 1=Reverse Pix On/Off)
A8h - Reserved
A9h - Set Power Save Mode (Osc=Off, LcdPower=Off, Outputs=VSS)
AAh - Reserved
ABh - Start Internal Oscillator (needed after reset)
ACh+(0..1) ? Unknown/Unused/Unspecified/Undocumented?
AEh+(0..1) - Set Display On/Off (0=Off, 1=On)
B0h 00h-15h Set Page Address (RAM page to be addressed, 0..21)
B0h 16h-FFh Reserved
B1h+(0..0Eh) 00h-FFh Same as above?
C0h+(0,8) - Set COM Output Scan Direction (0=Normal, 8=Remapped)
Remapped: COM(0..(N-1)) becomes COM(159..(159-N+1))
C1h+(0..6) - Same as above (Normal)
C9h+(0..6) - Same as above (Remapped)
D0h+(0..10h) - Reserved
E1h - Exit Power-save Mode (return from Sleep mode)
E2h - Software Reset (initialize some internal registers)
E3h - Reserved
E4h - Exit N-line Inversion mode
E5h - Reserved
E6h+(0..1) - Enable Scroll Buffer RAM
This command enable/disable the use of RAM page 20
and 21 during scrolling.
0=Enable Scroll Buffer RAM (POR)
1=Disable Scroll Buffer RAM
E8h 00h-FFh Set 3-line SPI Display Data Length (00h-FFh=1-256 bytes)
Specifies the number of bytes of display data to be
"written after this composite command." uh, which cmd?
E9h 00h-07h Set Temperature Coefficient for different LCD panels
(0,2,4,6 = -0.05%, -0.10%, -0.15%, -0.21% per 'C)
(1,3,5,7 = Reserved)
EAh+(0..5) - Reserved
F0h+(0..0Fh) ? Test mode commands and Extended features ;\
F0h+1 00h Test/extended? Used by Nintendo ; undocumented
F0h+7 02h Test/extended? Used by Nintendo ;
F0h+6 0Ah Test/extended? Used by Nintendo ;/
BELOW IS UNSORTED BLURB…
An 8bit status byte will be placed to the data bus if a read operation is performed if D/C is low. The status byte is defined as follow.
7 BUSY Chip is executing instruction (0=Ready, 1=Busy)
6 ON Display is On/Off (0=Off, 1=On)
5 RES Chip is executing reset (0=Ready, 1=Busy)
4-0 - Fixed (0Bh)
To read data from the GDDRAM, input High to R/W(WR) pin and D/C pin for 6800-series parallel mode. Low to E(RD) pin and High to D/C pin for 8080-series parallel mode. A complete data read cycle must issue two clocks to read both First Byte and Second Byte from GDDRAM. No data read is provided for serial mode. In normal mode, GDDRAM column address pointer will be increased by one automatically after each complete data read cycle. Also, a dummy read is required before the first data is read. See Figure 3 in Functional Description.
To write data to the GDDRAM, input Low to R/W(WR) pin and High to D/C pin for 6800-series parallel mode. High to E(RD) pin and Low to D/C pin for 8080-series parallel mode. A complete data write cycle must issue two clocks to write both First Byte and Second Byte to GDDRAM. For serial interface, it will always be in write mode. GDDRAM column address pointer will be increased by one automatically after each complete data write cycle. The column address will be reset to 0 in next data read/write operation is executed when it is 127.
Table 7 - Address Increment Table (Automatic)
D/C R/W(WR) Comment Address Increment
0 0 Write Command No
0 1 Read Status No
1 0 Write Data Yes, after each 2-byte pair
1 1 Read Data Yes, after each 2-byte pair
Address Increment is done automatically after two data read/write. The column address pointer of GDDRAM is also affected. It will be reset to 0 after 127. It should be noted that the page address will NOT be changed when this warp round happens.
Table 8 - Commands Required for R/W(WR) Actions on RAM
R/W(WR) Actions on RAMs Commands Required
Read/write Data from/to GDDRAM Set GDDRAM Page Address
Set GDDRAM Column Address
Read/Write Data
(1011XXXX)*
(X7X6X5X4X3X2X1X0)*
(00010X2X1X0)*
(0000X3X2X1X0)*
(X7X6X5X4X3X2X1X0)
* No need to resend the command again if it is set previously.
The read/write action to the Display Data RAM does not depend on the display mode. This means the user can change the RAM content whether the target RAM content is being displayed or not.
Official BMA150 register specs are in a Bosch-Captcha, which appears to be intended to fool humans and bots alike (text on hatched background, undefined color codes, increasingly unpleasant medusa-like details at closer look). Anyways, here’s a plaintext-hack of the Bosch-Captcha:
00h Chip ID (bit7-3=Unused, bit2-0=02h)
01h Version (bit7-4=al_version, bit3-0=ml_version) (undefined values)
02h Acc X Low (bit7-6=DataLsb, bit5-1=Unused, bit0=NewDataFlag)
03h Acc X High (bit7-0=DataMsb)
04h Acc Y Low (bit7-6=DataLsb, bit5-1=Unused, bit0=NewDataFlag)
05h Acc Y High (bit7-0=DataMsb)
06h Acc Z Low (bit7-6=DataLsb, bit5-1=Unused, bit0=NewDataFlag)
07h Acc Z High (bit7-0=DataMsb)
08h Temperature (bit7-0=DataTempMsb) (Lsb not existing, except in Trimming?)
09h Status Flags (see below)
0Ah Control Flags (see below)
0Bh Config Flags (see below)
0Ch LG Threshold (bit7-0)
0Dh LG Duration (bit7-0)
0Eh HG Threshold (bit7-0)
0Fh HG Duration (bit7-0)
10h Any Motion Threshold (bit7-0)
11h Misc Stuff (bit7-6=AnyMotionDur, bit5-3=HG Hyst, bit2-0=LG Hyst)
12h Customer Reserved 1 (bit7-0)
13h Customer Reserved 2 (bit7-0)
14h Range/Bandwidth (bit7-5=Reserved, bit4-3=Range, bit2-0=Bandwidth)
15h Misc Flags (see below)
16h Trimming X Low (bit7-6=OffsetLsb, bit5-0=Gain)
17h Trimming Y Low (bit7-6=OffsetLsb, bit5-0=Gain)
18h Trimming Z Low (bit7-6=OffsetLsb, bit5-0=Gain)
19h Trimming T Low (bit7-6=OffsetLsb, bit5-0=Gain)
1Ah Trimming X High (bit7-0=OffsetMsb)
1Bh Trimming Y High (bit7-0=OffsetMsb)
1Ch Trimming Z High (bit7-0=OffsetMsb)
1Dh Trimming T High (bit7-0=OffsetMsb)
1Eh-22h BST reserved (official blank/green)
23h BST reserved (official blank/white)
24h-2Ah Not used (official gray/dither)
2Bh-3Dh EEPROM Defaults for Registers 0Bh-1Dh
3Eh-42h BST reserved (official blank/orange)
43h-49h Not used (official gray/dither)
4Ah-4Fh Not mentioned (official not here)
50h-7Fh BST reserved (official blank/cyan)
Obscure Notes: Registers are 00h-06h,21h-22h,43h-7Fh are classifed as NOTHING, 07h-20h as IMAGE, and 23h-42h as EEPROM (whatever that crap means). Also, Registers 00h-15h are OPERATIONAL, 16h-3Dh are DEFAULT SETTING, 3Eh-7Fh are BOSCH SENSORTEC RESERVED.
7 ST Result
6-5 Not used (official piss/dither)
4 Alert Phase
3 LG_latched
2 HG_latched
1 LG_status
0 HG_status
7 Reserved (official gray/dither)
6 Reset INT
5 Update IMAGE
4 EE_W (uh? maybe eeprom write?)
3 Self Test 1
2 Self Test 0
1 Soft Reset
0 Sleep
7 Alert
6 Any Motion
5-4 Counter HG
3-2 Counter LG
1 Enable HG
0 Enable LG
7 SPI4
6 enable_adv_INT
5 new_data_INT
4 latch_INT
3 shadow_dis
2-1 wake_up_pause
0 wake_up
Case "Nintendo, NTR-031. PAT. PEND., IMWPN1J12"
PCB "DA A-4 IRU01-10" (two layers) plus "IRL01-01 "(brown extra film layer)
U1 32pin "S906748-1, SanDisk, 11014-64B, P0A837.00, 0843, NTR-IMWP-1" (ROM)
U2 32pin "38600R, A06V, AH00167, 0832" (CPU, ROM 8Kbyte, RAM 0.5KByte)
U3 5pin "?" (OR-gate? flipflop?) (for forwarding SPI /CS to FLASH /CS)
U4 8pin "45PE80VG, HPAMZ V5, KOR 833X, ST e3" (SPI FLASH 1024 Kbytes)
U1' 7pin "5 S.. 9" IR photodiode/phototransistor (on brown film layer)
X1 6pin "737Wv" ;7.37MHz? /FLASH.CS --|""""|-- GND
R1,R2,RA1 resistors | U3 |-- /SPI.CS (from NDS)
C1,C2,C3,C4,C5,C6 capacitors VDD33 --|____|-- U2.pinxxx
Note: The printed part number on the CPU is 38600R, which does officially have ROM 8Kbyte, RAM 0.5KByte (and the software does use only that much memory, but it does actually contain twice as much ROM and RAM, ie. it seems to be a 38602R chip… or, as the part number is wrong, it MIGHT even be a F38602 with FLASH memory?).
NTR-IMWJ Aruite Wakaru Seikatsu Rhythm (JP)
NTR-IMWE Personal Trainer: Walking (US)
NTR-IMWP Walk with Me (EU) (Laufrhythmus in german)
NTR-IA8P Active Health with Carol Vorderman (EU)
Case "Nintendo DS, NTR-027, (C) 2008 Nintendo, NTR-A-HC, Made in Japan"
Case "CE ./ VCI, ACN 060 566 083, Nintendo"
PCB "NTR-DHC-01" (in water resistant case)
Ux 32pin Side-A "38602R, F22V, AH04731, 0834" (CPU, ROM 16Kbyte, RAM 1KByte)
U2 8pin Side-B "564X, 48H3, 30" (SPI EEPROM 8Kbyte, ST M95640-W or similar)
U3 7pin Side-B "1 S. 9" IR photodiode/phototransistor or so
?? 2pin Side-A huge smd capacitor shaped thing, maybe analog 1-axis sensor?
?? 2pin Side-A huge smd capacitor shaped thing, maybe analog 1-axis sensor?
Ux/Qx Side-A many small chips with 3-6 pins and few markings
Xx 3pin Side-A "CB825" ;32.768-kHz or 38.4-kHz Crystal Resonator?
Yx 6pin Side-A ":i] 3.68t" ;3.68MHz (115.2kHz*32)
C1..C34 Plenty capacitors
R1..R28 Plenty resistors
BTI 2pin Side-B Battery holder (for CR2032 H, 3V)
Button Side-A Push button (communication button)
|< 4pin Side-A Two color LED
Activity Meter Instruction Booklet, 310 pages: “Do not disassemble or attempt to repair the Activity Meter yourself. Doing so could result in injury or electrocution.”
NTR-IPKx/NTR-IPGx P-letter HeartGold/SoulSilver
TWL-IRBO/TWL-IRAO P-letter Black/White
TWL-IREO/TWL-IRDO P-letter Black/White 2
Case "?"
PCB "NTR-PHC-01" (with green solder stop & unconventional black text layer)
U1 32pin Side-B "F38606, F04V, AK04052, 0942" (CPU,FLASH 48Kbyte,RAM 2KByte)
U2 4pin Side-A "?"
U3 4pin Side-A "?"
U4 4pin Side-A "M_RA"
U5 7pin Side-B IR photodiode/phototransistor or so
U6 8pin Side-A "Sxxxx, xxxx" (maybe SPI EEPROM?)
U7 12pin Side-B "043, A939, 021" (accelerometer?) (Bosch BMA150 ?)
U8 5pin Side-A "?"
Q1 6pin Side-A "Z4"
D1 3pin Side-A "?" dual diode or so
X1 3pin Side-B "EAJJ" ;32.768-kHz or 38.4-kHz Crystal Resonator?
Y1 6pin Side-B "3.68" ;3.68MHz (115.2kHz*32)
BZ1 2pin Side-B wires to piezo speaker (aka buzzer)
CN1 14pin Side-A LCD connector 14pin? or 2x14pin? (without backlight)
(with SSD1854 display controller inside of LCD screen)
(96x64 2-bit greyscale screen) (reportedly with SPI bus)
BT+/- Side-B Battery contacts for removeable battery (for CR2032, 3V)
C1..C29 Plenty capacitors
R1..R22 Plenty resistors
SW's Side-A Three buttons (left, center, right)
PCB "SAMU-01" (with green solder stop & unconventional black text layer)
U1 40pin Side-B "R5F101EEA, 1242KE415, SINGAPORE" (RL78 CPU)
U2 7pin Side-B "845G2947" IR photodiode/phototransistor or so, metal shield
U3 16pin Side-B (not installed)
U4 16pin Side-A --UNKNOWN MARKING, BAD PHOTO-- maybe accelerometer
U5 4pin Side-A --UNKNOWN MARKING, BAD PHOTO-- maybe SPI EEPROM/FLASH
U6 4pin Side-A (not installed)
U7 7pin Side-B "I357, U231, 094" whatever, in metal shielded case
X1 3pin Side-B "EABL" crystal or so
BZ1 2pin Side-B wires to piezo speaker (aka buzzer)
CN1 14pin Side-A LCD connector 14pin? or 2x14pin? (without backlight)
BT+/- Side-B Battery contacts for removeable battery
Q1 3pin Side-A Transistor or so
D1..D2 Side-A Diodes (3pin each)
C1..C29 Plenty capacitors
R1..R22 Plenty resistors
SW's Side-A Three buttons (left, center, right)
Similar as P-Walker, but uses a RL78 CPU (which is also found in 3DS MCU).
F020h FLMCR1 FLASH Memory Control 1
F021h FLMCR2 FLASH Memory Control 2
F022h FLPWCR FLASH Memory Power Control
F023h EBR1 FLASH Erase Block 1
F02Bh FENR FLASH Memory Enable
F067h RTCFLG RTC Interrupt Flag
F068h RSECDR RTC Seconds / Free running counter
F069h RMINDR RTC Minutes
F06Ah RHRDR RTC Hours
F06Bh RWKDR RTC Day-of-week
F06Ch RTCCR1 RTC Control 1
F06Dh RTCCR2 RTC Control 2
F06Fh RTCCSR RTC Clock Source Select
F078h ICCR1 I2C Bus Control 1
F079h ICCR2 I2C Bus Control 2
F07Ah ICMR I2C Bus Mode
F07Bh ICIER I2C Bus Interrupt Enable
F07Ch ICSR I2C Bus Status
F07Dh SAR I2C Slave Address
F07Eh ICDRT I2C Bus Transmit Data
F07Fh ICDRR I2C Bus Receive Data
F085h PFCR System Port Function Control
F086h PUCR8 Port 8 Pull-up Control
F087h PUCR9 Port 9 Pull-up Control
F08Ch PODR9 Port 9 Open-drain Control
F0D0h TMB1 Timer B1 Mode
F0D1h TC/LB1 Timer B1 Counter (R) / Load (W)
F0DCh CMCR0 Compare Control 0
F0DDh CMCR1 Compare Control 1
F0DEh CMDR Compare Data
F0E0h SSCRH SPI Synchronous Serial Control H (AccessState3)
F0E1h SSCRL SPI Synchronous Serial Control L (AccessState3)
F0E2h SSMR SPI Synchronous Serial Mode (AccessState3)
F0E3h SSER SPI Synchronous Serial Enable (AccessState3)
F0E4h SSSR SPI Synchronous Serial Status (AccessState3)
F0E9h SSRDR SPI Synchronous Serial Receive Data (AccessState3)
F0EBh SSTDR SPI Synchronous Serial Transmit Data (AccessState3)
F0F0h TMRW Timer W Mode
F0F1h TCRW Timer W Control
F0F2h TIERW Timer W Interrupt Enable
F0F3h TSRW Timer W Status
F0F4h TIOR0 Timer W I/O control 0
F0F5h TIOR1 Timer W I/O control 1
F0F6h TCNT Timer W Counter (16bit)
F0F8h GRA Timer W General A (16bit)
F0FAh GRB Timer W General B (16bit)
F0FCh GRC Timer W General C (16bit)
F0FEh GRD Timer W General D (16bit)
FF8Ch ECPWCR Async Event Counter PWM Compare (16bit)
FF8Eh ECPWDR Async Event Counter PWM Data (16bit)
FF91h SPCR IrDA UART Serial 3 Port Control
FF92h AEGSR Async Event Input Pin Edge Select
FF94h ECCR Async Event Counter Control
FF95h ECCSR Async Event Counter Control/Status
FF96h ECH Async Event Counter H
FF97h ECL Async Event Counter L
FF98h SMR3 IrDA UART Serial 3 Mode (AccessState3)
FF99h BRR3 IrDA UART Serial 3 Bit Rate (AccessState3)
FF9Ah SCR3 IrDA UART Serial 3 Control (AccessState3)
FF9Bh TDR3 IrDA UART Serial 3 Transmit Data (AccessState3)
FF9Ch SSR3 IrDA UART Serial 3 Status (AccessState3)
FF9Dh RDR3 IrDA UART Serial 3 Receive Data (AccessState3)
FFA6h SEMR IrDA UART Serial 3 Extended Mode (AccessState3)
FFA7h IrCR IrDA Control
FFB0h TMWD Timer WD Watchdog Mode
FFB1h TCSRWD1 Timer WD Watchdog Control/Status 1
FFB2h TCSRWD2 Timer WD Watchdog Control/Status 2
FFB3h TCWD Timer WD Watchdog Counter
FFBCh ADRR A/D Converter Result (16bit)
FFBEh AMR A/D Converter Mode
FFBFh ADSR A/D Converter Start
FFC0h PMR1 Port 1 Mode
FFC2h PMR3 Port 3 Mode
FFCAh PMRB Port B Mode
FFD4h PDR1 Port 1 Data
FFD6h PDR3 Port 3 Data
FFDBh PDR8 Port 8 Data
FFDCh PDR9 Port 9 Data
FFDEh PDRB Port B Data
FFE0h PUCR1 Port 1 Pull-up Control
FFE1h PUCR3 Port 3 Pull-up Control
FFE4h PCR1 Port 1 Control
FFE6h PCR3 Port 3 Control
FFEBh PCR8 Port 8 Control
FFECh PCR9 Port 9 Control
FFF0h SYSCR1 System Control 1
FFF1h SYSCR2 System Control 2
FFF2h IEGR Interrupt Edge Select
FFF3h IENR1 Interrupt Enable 1
FFF4h IENR2 Interrupt Enable 2
FFF5h OSCCR System Oscillator Control
FFF6h IRR1 Interrupt Flag 1
FFF7h IRR2 Interrupt Flag 2
FFFAh CKSTPR1 Clock Stop 1
FFFBh CKSTPR2 Clock Stop 2
Below are for Normal Mode with 16bit addressing (Extended Mode has 32bit vectors at 0000h..009Fh accordingly).
0000h Reset/Watchdog
0002h Reserved
0004h Reserved
0006h Reserved
0008h Reserved
000Ah Reserved
000Ch Reserved
000Eh External NMI interrupt
0010h Trap 0 opcode
0012h Trap 1 opcode
0014h Trap 2 opcode
0016h Trap 3 opcode
0018h Reserved
001Ah CPU Direct transition by executing SLEEP
001Ch Reserved
001Eh Reserved
0020h External IRQ0 interrupt
0022h External IRQ1 interrupt
0024h External IRQAEC interrupt
0026h Reserved
0028h Reserved
002Ah Comparator COMP0
002Ch Comparator COMP1
002Eh RTC per 0.25 seconds (4Hz) ;0.25-second overflow
0030h RTC per 0.5 seconds (2Hz) ;0.5-second overflow
0032h RTC per second (1Hz) ;Second periodic overflow
0034h RTC per minute ;Minute periodic overflow
0036h RTC per hour ;Hour periodic overflow
0038h RTC per day ;Day-of-week periodic overflow
003Ah RTC per week (7 days) ;Week periodic overflow
003Ch RTC Free-running overflow
003Eh WDT overflow (interval timer)
0040h Asynchronous event counter overflow
0042h Timer B1 Overflow
0044h Serial SPI (or IIC2) (aka I2C ?)
0046h Timer W Overflow or Capture/compare A,B,C,D
0048h Reserved
004Ah IrDA UART Serial 3
004Ch A/D Conversion end
004Eh Reserved
Note: The SSU (aka SPI) and IIC (aka I2C) share the same vector address. When using the IIC, shift the SSU to standby mode using CKSTPR2.
R0..R6 32bit General Purpose ;\can be alternately used as
R7 (SP) 32bit Stack Pointer ;/8bit/16bit registers (see below)
PC 24bit Program Counter
CCR 8bit Flags (occupies 16bit when pushed/stored in memory)
Registers R0..R7 can be split into 8bit/16bit registers (alike 80x86 registers):
.-----------------------.
| ERx | 32bit (ERx)
|-----------+-----------|
| Ex | Rx | 16bit (Rx)
'-----------+-----+-----|
' RxH | RxL | 8bit (RxB)
'-----'-----'
There are no opcodes for splitting upper 16bit in Ex into 8bit ExL,ExH (except, the sign/zero-extend opcodes seem to allow to extend ExL to Ex).
The CPU can be wired to two different addressing modes:
Normal Mode --> 16bit addressing (default)
Extended Mode --> 24bit addressing
Normal mode is usually used (the CPU has less than 64K ROM/RAM anyways), the opcodes with [er0..er7] memory addressing are then actually accessing [r0..r7], ie. the upper 16bit in e0..e7 are ignored (and can be used for general purpose data).
Extended mode, if it were ever used, uses the lower 24bit of er0..er7 for [er0..er7] addressing, and CALL/RET and indirect JMP opcodes are a bit slower, and exception vectors are 32bit wide.
Data is stored in Big-Endian. 16bit/32bit values must be stored at even addresses (with bit0 cleared) (there is no need to clear bit1 for 32bit values, namely, push/pop work regardless of bit1).
Native Nocash
@aa:8 [FFaa] Memory at FF00h..FFFFh (upper RAM and SFR's)
@aa:16 [nnnn]
@aa:24 [nnnnnn]
@Erm [Erm]
@(d:16,ERm) [ERm+nnnn]
@(d:24,ERm) [ERm+nnnnnn]
@ERm+ [ERm+] Memory access with post-increment
@-ERm [ERm-] Memory access with pre-decrement
(implied) [ER6],[ER5] Transfer to EEPROM (aka FLASH memory?)
Native Nocash
#nn:8,@aa:8 [FFaa].n
RnB,@aa:8 [FFaa].RnB
#nn:8,RdB RdB.n
RnB,RdB RdB.RnB
Note: The “#nn:8” suggests native syntax to use “#0x80” to select bit7, however, the existing disassembler does instead use “#7”.
All opcodes are multiples of 2 bytes (2,4,6,8,10 bytes), all opcodes should be always located at even addresses (ie. bit0 of jump address/disp operands should be always 0).
0..7 8bit Registers R0H..R7H (bit8-15) ;\RxB
8..F 8bit Registers R0L..R7L (bit0-7) ;/
0..7 16bit Registers R0..R7 (bit0-15) ;\Rx
8..F 16bit Registers E0..E7 (bit16-31) ;/
0..7 32bit Registers ER0..ER7 (bit0-31) ;-ERx (in normal opcodes)
8..F 32bit Registers ER0..ER7 (bit0-31) ;-ERx (in opcodes marked *m,*s)
Opcode Native Nocash States IxHUNZVC
0.. --> Misc 0xxx
1.. --> Misc 1xxx
2dnn MOV.B @aa:8,RdB MOV.B RdB,[FFaa] 4 ----nz0-
3snn MOV.B Rs,@aa:8 MOV.B [FFaa],Rs 4 ----nz0-
4cnn --> Jumps (relative 8bit range)
5.. --> Jumps (various) and unsigned mul/div
6.. --> Misx 6xxx
7.. --> Misc 7xxx
8dnn ADD.B #nn:8,RdB ADD.B RdB,nn 2 --h-nzvc
9dnn ADDX #nn:8,RdB ADC.B RdB,nn 2 --h-nzvc
Adnn CMP.B #nn:8,RdB CMP.B RdB,nn 2 --h-nzvc
Bdnn SUBX #nn:8,RdB SBC.B RdB,nn 2 --h-nzvc
Cdnn OR.B #nn:8,RdB OR.B RdB,nn 2 ----nz0-
Ddnn XOR.B #nn:8,RdB XOR.B RdB,nn 2 ----nz0-
Ednn AND.B #nn:8,RdB AND.B RdB,nn 2 ----nz0-
Fdnn MOV.B #nn:8,RdB MOV.B RdB,nn 2 ----nz0-
0000 NOP NOP 2 --------
01.. --> Misc 01xx ;Memory Load/Store (32bit ERn) etc.
020d STC.B CCR,RdB MOV.B RdB,CCR 2 --------
030s LDC.B RsB,CCR MOV.B CCR,RsB 2 xxxxxxxx
04nn ORC #nn:8,CCR OR.B CCR,nn 2 xxxxxxxx
05nn XORC #nn:8,CCR XOR.B CCR,nn 2 xxxxxxxx
06nn ANDC #nn:8,CCR AND.B CCR,nn 2 xxxxxxxx
07nn LDC.B #nn:8,CCR MOV.B CCR,nn 2 xxxxxxxx
08sd ADD.B RsB,RdB ADD.B RdB,RsB 2 --h-nzvc
09sd ADD.W Rs,Rd ADD.W Rd,Rs 2 --h-nzvc
0A.. --> Increment/Add
0B.. --> Increment/Add
0Csd MOV.B RsB,RdB MOV.B RdB,RsB 2 ----nz0-
0Dsd MOV.W Rs,Rd MOV.W Rd,Rs 2 ----nz0-
0Esd ADDX RsB,RdB ADC.B RdB,RsB 2 --h-nzvc
0F0d DAA RdB DAA.B RdB 2 --U-nzUc
0Fsd *s MOV.L ERs,ERd MOV.L ERd,ERs 2 ----nz0-
010069md MOV.L @ERm,ERd MOV.L ERd,[ERm] 8 ----nz0-
014069m0 LDC.W @ERm,CCR MOV.W CCR,[ERm] 6 xxxxxxxx
010069ms *m MOV.L ERs,@ERm MOV.L [ERm],ERs 8 ----nz0-
014069m0 *m STC.W CCR,@ERm MOV.W [ERm],CCR 6 --------
01006B0dnnnn MOV.L @aa:16,ERd MOV.L ERd,[nnnn] 10 ----nz0-
01406B00nnnn LDC.W @aa:16,CCR MOV.W CCR,[nnnn] 8 xxxxxxxx
01006B2d00nnnnnn MOV.L @aa:24,ERd MOV.L ERd,[nnnnnn] 12 ----nz0-
01406B2000nnnnnn LDC.W @aa:24,CCR MOV.W CCR,[nnnnnn] 10 xxxxxxxx
01006B8snnnn MOV.L ERs,@aa:16 MOV.L [nnnn],ERs 10 ----nz0-
01406B80nnnn STC.W CCR,@aa:16 MOV.W [nnnn],CCR 8 --------
01006BAs00nnnnnn MOV.L ERs,@aa:24 MOV.L [nnnnnn],ERs 12 ----nz0-
01406BA000nnnnnn STC.W CCR,@aa:24 MOV.W [nnnnnn],CCR 10 --------
01006Dmd MOV.L @ERm+,ERd MOV.L ERd,[ERm+] 10 ----nz0-
01406Dm0 LDC.W @ERm+,CCR MOV.W CCR,[ERm+] 8 xxxxxxxx
01006Dms *m MOV.L ERs,@-ERm MOV.L [ERm-],ERs 10 ----nz0-
01406Dm0 *m STC.W CCR,@-ERm MOV.W [ERm-],CCR 8 --------
01006Fmdnnnn MOV.L @(d:16,ERm),ERd MOV.L ERd,[ERm+nnnn] 10 ----nz0-
01406Fm0nnnn LDC.W @(d:16,ERm),CCR MOV.W CCR,[ERm+nnnn] 8 xxxxxxxx
01006Fmsnnnn *m MOV.L ERs,@(d:16,ERm) MOV.L [ERm+nnnn],ERs 10 ----nz0-
01406Fm0nnnn *m STC.W CCR,@(d:16,ERm) MOV.W [ERm+nnnn],CCR 8 --------
010078m06B2d00.. MOV.L @(d:24,ERm),ERd MOV.L ERd,[ERm+nnnnnn] 14 ----nz0-
014078m06B2000.. LDC.W @(d:24,ERm),CCR MOV.W CCR,[ERm+nnnnnn] 12 xxxxxxxx
010078m06BAs00..*? MOV.L ERs,@(d:24,ERm) MOV.L [ERm+nnnnnn],ERs 14 ----nz0-
014078m06BA000.. STC.W CCR,@(d:24,ERm) MOV.W [ERm+nnnnnn],CCR 12 --------
0180 SLEEP HALT 2 --------
01C050sd MULXS.B RsB,Rd SMUL.B Rd,RsB 16 ----nz--
01C052sd MULXS.W Rs,ERd SMUL.W ERd,Rs 24 ----nz--
01D051sd DIVXS.B RsB,Rd SDIV.B Rd,RsB 16 ----nz--
01D053sd DIVXS.W Rs,ERd SDIV.W ERd,Rs 24 ----nz--
01F064sd OR.L ERs,ERd OR.L ERd,ERs 4 ----nz0-
01F065sd XOR.L ERs,ERd XOR.L ERd,ERs 4 ----nz0-
01F066sd AND.L E?Rs,ERd AND.L ERd,ERs 4 ----nz0-
10.. --> Shift/Rotate (shift left)
11.. --> Shift/Rotate (shift right)
12.. --> Shift/Rotate (rotate left)
13.. --> Shift/Rotate (rotate right)
14sd OR.B RsB,RdB OR.B RdB,RsB 2 ----nz0-
15sd XOR.B RsB,RdB XOR.B RdB,RsB 2 ----nz0-
16sd AND.B RsB,RdB AND.B RdB,RsB 2 ----nz0-
17.. --> Not/Neg/Extend
18sd SUB.B RsB,RdB SUB.B RdB,RsB 2 --h-nzvc
19sd SUB.W Rs,Rd SUB.W Rd,Rs 2 --h-nzvc
1A.. --> Decrement/Subtract
1B.. --> Decrement/Subtract
1Csd CMP.B RsB,RdB CMP.B RdB,RsB 2 --h-nzvc
1Dsd CMP.W Rs,Rd CMP.W Rd,Rs 2 --h-nzvc
1Esd SUBX RsB,RdB SBC.B RdB,RsB 2 --h-nzvc
1F0d DAS RdB DAS.B RdB 2 --U-nzU?
1Fsd *s CMP.L ERs,ERd CMP.L ERd,ERs 2 --h-nzvc
100d SHLL.B RdB SHL.B RdB 2 ----nz0c
101d SHLL.W Rd SHL.W Rd 2 ----nz0c
103d SHLL.L ERd SHL.L ERd 2 ----nz0c
108d SHAL.B RdB SAL.B RdB 2 ----nzvc
109d SHAL.W Rd SAL.W Rd 2 ----nzvc
10Bd SHAL.L ERd SAL.L ERd 2 ----nzvc
110d SHLR.B RdB SHR.B RdB 2 ----0z0c
111d SHLR.W Rd SHR.W Rd 2 ----0z0c
113d SHLR.L ERd SHR.L ERd 2 ----0z0c
118d SHAR.B RdB SAR.B RdB 2 ----nz0c
119d SHAR.W Rd SAR.W Rd 2 ----nz0c
11Bd SHAR.L ERd SAR.L ERd 2 ----nz0c
120d ROTXL.B RdB RCL.B RdB 2 ----nz0c
121d ROTXL.W Rd RCL.W Rd 2 ----nz0c
123d ROTXL.L ERd RCL.L ERd 2 ----nz0c
128d ROTL.B RdB ROL.B RdB 2 ----nz0c
129d ROTL.W Rd ROL.W Rd 2 ----nz0c
12Bd ROTL.L ERd ROL.L ERd 2 ----nz0c
130d ROTXR.B RdB RCR.B RdB 2 ----nz0c
131d ROTXR.W Rd RCR.W Rd 2 ----nz0c
133d ROTXR.L ERd RCR.L ERd 2 ----nz0c
138d ROTR.B RdB ROR.B RdB 2 ----nz0c
139d ROTR.W Rd ROR.W Rd 2 ----nz0c
13Bd ROTR.L ERd ROR.L ERd 2 ----nz0c
170d NOT.B RdB NOT.B RdB 2 ----nz0-
171d NOT.W Rd NOT.W Rd 2 ----nz0-
173d NOT.L Rd NOT.L ERd 2 ----nz0-
175d EXTU.W Rd UMOV Rd,RdL ;or Ed,EdL? 2 ----0z0-
177d EXTU.L ERd UMOV ERd,Rd 2 ----0z0-
178d NEG.B RdB NEG.B RdB 2 --h-nzvc
179d NEG.W Rd NEG.W Rd 2 --h-nzvc
17Bd NEG.L Rd NEG.L ERd 2 --h-nzvc
17Dd EXTS.W Rd SMOV Rd,RdL ;or Ed,EdL? 2 ----nz0-
17Fd EXTS.L ERd SMOV ERd,Rd 2 ----nz0-
0A0d INC.B RdB INC.B RdB,1 2 ----nzv-
1A0d DEC.B RdB DEC.B RdB,1 2 ----nzv-
0Asd *s ADD.L E?Rs,ERd ADD.L ERd,ERs 2 --h-nzvc
1Asd *s SUB.L ERs,ERd SUB.L ERd,ERs 2 --h-nzvc
0B0d ADDS #1,ERd INC.S ERd,1 2 --------
1B0d SUBS #1,ERd DEC.S ERd,1 2 --------
0B5d INC.W #1,Rd INC.W Rd,1 2 ----nzv-
1B5d DEC.W #1,Rd DEC.W Rd,1 2 ----nzv-
0B7d INC.L #1,ERd INC.L ERd,1 2 ----nzv-
1B7d DEC.L #1,ERd DEC.L ERd,1 2 ----nzv-
0B8d ADDS #2,ERd INC.S ERd,2 2 --------
1B8d SUBS #2,ERd DEC.S ERd,2 2 --------
0B9d ADDS #4,ERd INC.S ERd,4 2 --------
1B9d SUBS #4,ERd DEC.S ERd,4 2 --------
0BDd INC.W #2,Rd INC.W Rd,2 2 ----nzv-
1BDd DEC.W #2,Rd DEC.W Rd,2 2 ----nzv-
0BFd INC.L #2,ERd INC.L ERd,2 2 ----nzv-
1BFd DEC.L #2,ERd DEC.L ERd,2 2 ----nzv-
50sd MULXU.B RsB,Rd UMUL.B Rd,RsB 14 ----nz--
51sd DIVXU.B RsB,Rd UDIV.B Rd,RsB 14 ----nz--
52sd MULXU.W Rs,ERd UMUL.W ERd,Rs 22 ----nz--
53sd DIVXU.W Rs,ERd UDIV.W ERd,Rs 22 ----nz--
5470 RTS RET 8,10 --------
55nn BSR d:8 CALL $+/-nn 6,8 --------
5670 RTE RETI 10 xxxxxxxx
57n0 TRAPA #n:2 TRAP 0..3 ;[0010h+n*2] 14 1x------
58c0nnnn --> Jumps (relative 16bit range)
59s0 JMP @ERs JMP ERs 4 --------
5Annnnnn JMP @aa:24 JMP nnnnnn 6 --------
5Baa JMP @@aa:8 JMP [FFaa] 8,10 --------
5C00nnnn BSR d:16 CALL $+/-nnnn 8,10 --------
5Ds0 JSR @ERs CALL ERs 6,8 --------
5Ennnnnn JSR @aa:24 CALL nnnnnn 8,10 --------
5Faa JSR @@aa:8 CALL [FFaa] 8,12 --------
4cnn Bcc d:8 Jcc $+/-nn 4 --------
58c0nnnn Bcc d:16 Jcc $+/-nnnn 6 --------
The 4bit condition code can be:
0 BRA or BT JMP ;always/true
1 BRN or BF - ;never/false
2 BHI JA ;unsigned-above
3 BLS JBE ;unsigned-below-equal
4 BCC or BHS JNC or JAE ;unsigned-above-equal
5 BCS or BLO JC or JB ;unsigned-below
6 BNE JNZ or JNE ;not equal/zero
7 BEQ JZ or JE ;equal/zero
8 BVC JNO ;signed-no overflow
9 BVS JO ;signed-n-overflow
A BPL JNS ;signed-n-plus
B BMI JS ;signed-n-minus
C BGE JGE ;signed-n-greater-eq
D BLT JL ;signed-n-less
E BGT JG ;signed-n-greater
F BLE JLE ;signed-n-less-equal
Destination address should be equal (although, the opcodes are weirdly using byte offsets, hence limiting 8bit range to even 7bit range).
The execution time for condition=false is unknown. The time for BRN (always false) is said to be equal to two NOPs (but unknown why one would use that opcode, and if it does refer to the BRN opcode with 8bit and/or 16bit range).
60nd BSET RnB,RdB SET RdB.RnB 2 --------
61nd BNOT RnB,RdB NOT RdB.RnB 2 --------
62nd BCLR RnB,RdB CLR RdB.RnB 2 --------
63nd BTST RnB,RdB TST RdB.RnB 2 -----z--
64sd OR.W Rs,Rd OR.W Rd,Rs 2 ----nz0-
65sd XOR.W Rs,Rd XOR.W Rd,Rs 2 ----nz0-
66sd AND.W Rs,Rd AND.W Rd,Rs 2 ----nz0-
67nd *i B{I}ST #nn:8,RdB MOV RdB.n,{not} C 2 --------
68md MOV.B @ERm,RdB MOV.B RdB,[ERm] 4 ----nz0-
68ms *m MOV.B RsB,@ERm MOV.B [ERm],RsB 4 ----nz0-
69md MOV.W @ERm,Rd MOV.W Rd,[ERm] 4 ----nz0-
69ms *m MOV.W Rs,@ERm MOV.W [ERm],Rs 4 ----nz0-
6A0dnnnn MOV.B @aa:16,RdB MOV.B RdB,[aaaa] 6 ----nz0-
6A2d00nnnnnn MOV.B @aa:24,RdB MOV.B RdB,[aaaaaa] 8 ----nz0-
6A4dnnnn MOVFPE @aa:16,RdB MOV.B RdB,[periph:aaaa] * ----nz0-
6A8snnnn MOV.B RsB,@aa:16 MOV.B [aaaa],RsB 6 ----nz0-
6AAs00nnnnnn MOV.B RsB,@aa:24 MOV.B [aaaaaa],RsB 8 ----nz0-
6ACsnnnn MOVTPE RsB,@aa:16 MOV.B [periph:aaaa],RsB * ----nz0-
6B0dnnnn MOV.W @aa:16,Rd MOV.W Rd,[aaaa] 6 ----nz0-
6B2d00nnnnnn MOV.W @aa:24,Rd MOV.W Rd,[aaaaaa] 8 ----nz0-
6B8snnnn MOV.W Rs,@aa:16 MOV.W [aaaa],Rs 6 ----nz0-
6BAs00nnnnnn MOV.W Rs,@aa:24 MOV.W [aaaaaa],Rs 8 ----nz0-
6Cmd MOV.B @ERm+,RdB MOV.B RdB,[ERm+] 6 ----nz0-
6Cms *m MOV.B RsB,@-ERm MOV.B [ERm-],RsB 6 ----nz0-
6Dmd MOV.W @ERm+,RdB MOV.W RdB,[ERm+] 6 ----nz0-
6Dms *m MOV.W RsB,@-ERm MOV.W [ERm-],RsB 6 ----nz0-
6Emdnnnn MOV.B @(d:16,ERm),RdB MOV.B RdB,[ERm+nnnn] 6 ----nz0-
6Emsnnnn *m MOV.B RsB,@(d:16,ERm) MOV.B [ERm+nnnn],RsB 6 ----nz0-
6Fmdnnnn MOV.W @(d:16,ERm),Rd MOV.W Rd,[ERm+nnnn] 6 ----nz0-
6Fmsnnnn *m MOV.W Rs,@(d:16,ERm) MOV.W [ERm+nnnn],Rs 6 ----nz0-
70nd BSET #nn:8,RdB SET RdB.n 2 --------
71nd BNOT #nn:8,RdB NOT RdB.n 2 --------
72nd BCLR #nn:8,RdB CLR RdB.n 2 --------
73nd BTST #nn:8,RdB TST RdB.n 2 -----z--
74nd *i B{I}OR #nn:8,RdB OR C,{not} RdB.n 2 -------c
75nd *i B{I}XOR #nn:8,RdB XOR C,{not} RdB.n 2 -------c
76nd *i B{I}AND #nn:8,RdB AND C,{not} RdB.n 2 -------c
77nd *i B{I}LD #nn:8,RdB MOV C,{not} RdB.n 2 -------c
78m06A2d00nnnnnn MOV.B @(d:24,ERm),RdB MOV.B RdB,[ERm+nnnnnn] 10 ----nz0-
78m06AAs00nnnnnn MOV.B RsB,@(d:24,ERm) MOV.B [ERm+nnnnnn],RsB 10 ----nz0-
78m06B2d00nnnnnn MOV.W @(d:24,ERm),Rd MOV.W Rd,[ERm+nnnnnn] 10 ----nz0-
78m06BAs00nnnnnn*? MOV.W Rs,@(d:24,ERm) MOV.W [ERm+nnnnnn],Rs 10 ----nz0-
79.. --> Immediate (16bit)
7A.. --> Immediate (32bit)
7B5C498F EEPMOV.B LDIR [ER6],[ER5],R4L 8+4n --------
7BD4598F EEPMOV.W LDIR [ER6],[ER5],R4 8+4n --------
7C.. --> Bit Operations (Memory at ERm)
7D.. --> Bit Operations (Memory at ERm)
7E.. --> Bit Operations (Memory at FFaa)
7F.. --> Bit Operations (Memory at FFaa)
790dnnnn MOV.W #nnnn:16,Rd MOV.W Rd,nnnn 4 ----nz0-
791dnnnn ADD.W #nnnn:16,Rd ADD.W Rd,nnnn 4 --h-nzvc
792dnnnn CMP.W #nnnn:16,Rd CMP.W Rd,nnnn 4 --h-nzvc
793dnnnn SUB.W #nnnn:16,Rd SUB.W Rd,nnnn 4 --h-nzvc
794dnnnn OR.W #nnnn:16,Rd OR.W Rd,nnnn 4 ----nz0-
795dnnnn XOR.W #nnnn:16,Rd XOR.W Rd,nnnn 4 ----nz0-
796dnnnn AND.W #nnnn:16,Rd AND.W Rd,nnnn 4 ----nz0-
7A0dnnnnnnnn MOV.L #nnnnnnnn:32,E?Rd MOV.L E?Rd,nnnnnnnn 6 ----nz0-
7A1dnnnnnnnn ADD.L #nnnnnnnn:32,ERd ADD.L ERd,nnnnnnnn 6 --h-nzvc
7A2dnnnnnnnn CMP.L #nnnnnnnn:32,ERd CMP.L ERd,nnnnnnnn 6 --h-nzvc
7A3dnnnnnnnn SUB.L #nnnnnnnn:32,ERd SUB.L ERd,nnnnnnnn 6 --h-nzvc
7A4dnnnnnnnn OR.L #nnnnnnnn:32,ERd OR.L ERd,nnnnnnnn 6 ----nz0-
7A5dnnnnnnnn XOR.L #nnnnnnnn:32,ERd XOR.L ERd,nnnnnnnn 6 ----nz0-
7A6dnnnnnnnn AND.L #nnnnnnnn:32,ERd AND.L ERd,nnnnnnnn 6 ----nz0-
7Cm074n0 *i B{I}OR #nn:8,@ERm OR C,{not} [ERm].n 6 -------c
7Cm075n0 *i B{I}XOR #nn:8,@ERm XOR C,{not} [ERm].n 6 -------c
7Cm076n0 *i B{I}AND #nn:8,@ERm AND C,{not} [ERm].n 6 -------c
7Cm077n0 *i B{I}LD #nn:8,@ERm MOV C,{not} [ERm].n 6 -------c
7Dm060n0 BSET RnB,@ERm SET [ERm].RnB 8 --------
7Dm061n0 BNOT RnB,@ERm NOT [ERm].RnB 8 --------
7Dm062n0 BCLR RnB,@ERm CLR [ERm].RnB 8 --------
7Dm063n0 BTST RnB,@ERm TST [ERm].RnB 8 -----z--
7Dm067n0 *i B{I}ST #nn:8,@ERm MOV [ERm].n,{not} C 8 --------
7Dm070n0 BSET #nn:8,@ERm SET [ERm].n 8 --------
7Dm071n0 BNOT #nn:8,@ERm NOT [ERm].n 8 --------
7Dm072n0 BCLR #nn:8,@ERm CLR [ERm].n 8 --------
7Dm073n0 BTST #nn:8,@ERm TST [ERm].n 8 -----z--
7Eaa74n0 *i B{I}OR #nn:8,@aa:8 OR C,{not} [FFaa].n 6 -------c
7Eaa75n0 *i B{I}XOR #nn:8,@aa:8 XOR C,{not} [FFaa].n 6 -------c
7Eaa76n0 *i B{I}AND #nn:8,@aa:8 AND C,{not} [FFaa].n 6 -------c
7Eaa77n0 *i B{I}LD #nn:8,@aa:8 MOV C,{not} [FFaa].n 6 -------c
7Faa60n0 BSET RnB,@aa:8 SET [FFaa].RnB 8 --------
7Faa61n0 BNOT RnB,@aa:8 NOT [FFaa].RnB 8 --------
7Faa62n0 BCLR RnB,@aa:8 CLR [FFaa].RnB 8 --------
7Faa63n0 BTST RnB,@aa:8 TST [FFaa].RnB 8 -----z--
7Faa67n0 *i B{I}ST #nn:8,@aa:8 MOV [FFaa].n,{not} C 8 --------
7Faa70n0 BSET #nn:8,@aa:8 SET [FFaa].n 8 --------
7Faa71n0 BNOT #nn:8,@aa:8 NOT [FFaa].n 8 --------
7Faa72n0 BCLR #nn:8,@aa:8 CLR [FFaa].n 8 --------
7Faa73n0 BTST #nn:8,@aa:8 TST [FFaa].n 8 -----z--
Nintendo uses undocumented opcode 7Eaa73n0 instead of official 7Faa73n0. Unknown if both are working. Unknown if all other opcodes in 7EaaXXn0 range do also act like 7FaaXXn0, and perhaps also 7Cm0XXn0 like 7Dm0XXn0 (nintendo uses the offical 7D/7F for SET/NOT/CLR, so weirdness may apply for TST only).
*i optional inverted source operand (when setting bit3 in the "n" digit)
*s must have bit3 set in "s" digit
*m must have bit3 set in "m" digit
*? must have bit3 set-or-not-set (has conflicting info in official specs)
E?Rs meant to be ERs (although official specs omit the E in some cases)
E?Rd meant to be ERd (although official specs omit the E in some cases)
xxxS meant to be Silent, no flags affected (although specs say Sign Extend)
xxxX meant to mean Carry, or meant to mean nothing specific in other cases
The official “PUSH/POP.W/L Rn” opcodes are normal MOV.W/L opcodes with ER7 (SP) and post-increment or pre-decrement.
6DFn PUSH.W Rn ;MOV.W [ER7-],Rn
6D7n POP.W Rn ;MOV.W Rn,[ER7+]
01006DFn PUSH.L ERn ;MOV.L [ER7-],ERn
01006D7n POP.L ERn ;MOV.L ERn,[ER7+]
Although not officially defined, one could also implement “PUSH/POP.W CCR”.
There are no PUSH/POP.B opcodes (because that would misalign the stack).
---N/A--- MOV.L @aa:8,ERd MOV.L ERd,[FFaa] - ----nz0-
---N/A--- MOV.L ERs,@aa:8 MOV.L [FFaa],ERs - ----nz0-
---N/A--- MOV.W @aa:8,Rd MOV.W Rd,[FFaa] - ----nz0-
---N/A--- MOV.W Rs,@aa:8 MOV.W [FFaa],Rs - ----nz0-
---N/A--- SUB.B #nn:8,RdB SUB.B RdB,nn - --h-nzvc
Some NDS games (eg. Warioware D.I.Y.) contain NAND memory, this memory contains both the game and save memory (normal NDS games contain separate ROM and FLASH/EEPROM chips for that purposes) (the advantage is that NAND allows more storage than the usual FLASH chips).
The Warioware D.I.Y. PCB is marked “DI X-7 C17-01”, and it does contain only one single chip, marked “SAMSUNG 004, KLC2811ANB-P204, NTR-UORE-0”.
That NAND chip connects directly to the NDS parallel bus (the serial SPI chipselect is left unconnected). Unknown how to write to the chip, and unknown if certain regions are write-protected.
The DS Vision cartridge contains a built-in microSD card slot. Users can download videos from internet (against a fee), store the videos on microSD cards, and then view them on the NDS via DS Vision cartridge.
Unknown how the microSD is accessed; via parallel ‘ROM’ bus and/or via serieal SPI bus; by which commands? Also unknown if the thing contains built-in video decoder hardware, or if videos are decoded on ARM cpus.
The first commercial DS cheat code solution, this device was developed by Datel. It supports swapping out cartridges after loading the AR software. For updating, the user may either manually enter codes or use the included proprietary USB cable that comes with the device. The user has been able to manually update codes since firmware version 1.52.
ABCD-NNNNNNNN Game ID ;ASCII Gamecode [00Ch] and CRC32 across [0..1FFh]
00000000 XXXXXXXX manual hook codes (rarely used) (default is auto hook)
0XXXXXXX YYYYYYYY word[XXXXXXX+offset] = YYYYYYYY
1XXXXXXX 0000YYYY half[XXXXXXX+offset] = YYYY
2XXXXXXX 000000YY byte[XXXXXXX+offset] = YY
3XXXXXXX YYYYYYYY IF YYYYYYYY > word[XXXXXXX] ;unsigned ;\
4XXXXXXX YYYYYYYY IF YYYYYYYY < word[XXXXXXX] ;unsigned ; for v1.54,
5XXXXXXX YYYYYYYY IF YYYYYYYY = word[XXXXXXX] ; when X=0,
6XXXXXXX YYYYYYYY IF YYYYYYYY <> word[XXXXXXX] ; uses
7XXXXXXX ZZZZYYYY IF YYYY > ((not ZZZZ) AND half[XXXXXXX]) ; [offset]
8XXXXXXX ZZZZYYYY IF YYYY < ((not ZZZZ) AND half[XXXXXXX]) ; instead of
9XXXXXXX ZZZZYYYY IF YYYY = ((not ZZZZ) AND half[XXXXXXX]) ; [XXXXXXX]
AXXXXXXX ZZZZYYYY IF YYYY <> ((not ZZZZ) AND half[XXXXXXX]) ;/
BXXXXXXX 00000000 offset = word[XXXXXXX+offset]
C0000000 YYYYYYYY FOR loopcount=0 to YYYYYYYY ;execute Y+1 times
C4000000 00000000 offset = address of the C4000000 code ;v1.54
C5000000 XXXXYYYY counter=counter+1, IF (counter AND YYYY) = XXXX ;v1.54
C6000000 XXXXXXXX [XXXXXXXX]=offset ;v1.54
D0000000 00000000 ENDIF
D1000000 00000000 NEXT loopcount
D2000000 00000000 NEXT loopcount, and then FLUSH everything
D3000000 XXXXXXXX offset = XXXXXXXX
D4000000 XXXXXXXX datareg = datareg + XXXXXXXX
D5000000 XXXXXXXX datareg = XXXXXXXX
D6000000 XXXXXXXX word[XXXXXXXX+offset]=datareg, offset=offset+4
D7000000 XXXXXXXX half[XXXXXXXX+offset]=datareg, offset=offset+2
D8000000 XXXXXXXX byte[XXXXXXXX+offset]=datareg, offset=offset+1
D9000000 XXXXXXXX datareg = word[XXXXXXXX+offset]
DA000000 XXXXXXXX datareg = half[XXXXXXXX+offset]
DB000000 XXXXXXXX datareg = byte[XXXXXXXX+offset] ;bugged on pre-v1.54
DC000000 XXXXXXXX offset = offset + XXXXXXXX
EXXXXXXX YYYYYYYY Copy YYYYYYYY parameter bytes to [XXXXXXXX+offset...]
44332211 88776655 parameter bytes 1..8 for above code (example)
0000AA99 00000000 parameter bytes 9..10 for above code (padded with 00s)
FXXXXXXX YYYYYYYY Copy YYYYYYYY bytes from [offset..] to [XXXXXXX...]
IF/ENDIF can be nested up to 32 times. FOR/NEXT cannot be nested, any FOR statement does forcefully terminate any prior loop. FOR does backup the current IF condidition flags, and NEXT does restore these flags, so ENDIF(s) aren’t required inside of the loop. The NEXT+FLUSH command does (after finishing the loop) reset offset=0, datareg=0, and does clear all condition flags, so further ENDIF(s) aren’t required after the loop.
Before v1.54, the DB000000 code did accidently set offset=offset+XXXXXXX after execution of the code. For all word/halfword accesses, the address should be aligned accordingly. For the COPY commands, addresses should be aligned by four (all data is copied with ldr/str, except, on odd lengths, the last 1..3 bytes do use ldrb/strb).
offset, datareg, loopcount, and counter are internal registers in the action replay software.
The condition register is checked, for all code types
but the D0, D1 and D2 code type
Makes sense.
and for the C5 code type it’s checked AFTER the counter has
been incremented (so the counter is always incremented
I love that exceptions ;-)
The hook codes consist of a series of nine 00000000 XXXXXXXX codes, and must be marked as (M) code (for not being confused with normal 0XXXXXXX YYYYYYYY codes). For all nine codes, the left 32bit are actually don’t care (but should be zero), the meaning of the right 32bit varies from 1st to 9th code.
1st: Address used prior to launching game (eg. 23xxxxxh)
2nd: Address to write the hook at (inside the ARM7 executable)
3rd: Hook final address (huh?)
4th: Hook mode selection (0=auto, 1=mode1, 2=mode2)
5th: Opcode that replaces the hooked one (eg. E51DE004h)
6th: Address to store important stuff (default 23FE000h)
7th: Address to store the code handler (default 23FE074h)
8th: Address to store the code list (default 23FE564h)
9th: Must be 1 (00000001h)
For most games, the AR does automatically hook code on the ARM7. Doing that automatically is nice, but hooking ARM7 means that there is no access to VRAM, TCM and Cache, which <might> cause problems since efficient games <should> store all important data in TCM or Cache (though, in practice, I’d doubt that any existing NDS games are that efficient).
To Kenobi and Dualscreenman from Kodewerx for above ARDS cheat info.
This is Pelican’s entry into the DS cheat-device industry. It supports swapping out the cartridges, and alternately, also gives the user the option of connecting another gamecard onto it. For updating, the user may either manually enter codes, or use Wifi to connect to the Codebreaker update site (that updating will overwrite all manually entered codes though).
---Initialization---
0000CR16 GAMECODE Specify Game ID, use Encrypted codes
8000CR16 GAMECODE Specify Game ID, use Unencrypted codes
BEEFC0DE XXXXXXXX Change Encryption Keys
A0XXXXXX YYYYYYYY Bootup-Hook 1, X=Address, Y=Value
A8XXXXXX YYYYYYYY Bootup-Hook 2, X=Address, Y=Value
F0XXXXXX TYYYYYYY Code-Hook 1 (T=Type,Y=CheatEngineAddr,X=HookAddr)
F8XXXXXX TPPPPPPP Code-Hook 2 (T=Type,X=CheatEngineHookAddr,P=Params)
---General codes---
00XXXXXX 000000YY [X]=YY
10XXXXXX 0000YYYY [X]=YYYY
20XXXXXX YYYYYYYY [X]=YYYYYYYY
60XXXXXX 000000YY ZZZZZZZZ 00000000 [[X]+Z]=YY
60XXXXXX 0000YYYY ZZZZZZZZ 10000000 [[X]+Z]=YYYY
60XXXXXX YYYYYYYY ZZZZZZZZ 20000000 [[X]+Z]=YYYYYYYY
30XXXXXX 000000YY [X]=[X] + YY
30XXXXXX 0001YYYY [X]=[X] + YYYY
38XXXXXX YYYYYYYY [X]=[X] + YYYYYYYY
70XXXXXX 000000YY [X]=[X] OR YY
70XXXXXX 001000YY [X]=[X] AND YY
70XXXXXX 002000YY [X]=[X] XOR YY
70XXXXXX 0001YYYY [X]=[X] OR YYYY
70XXXXXX 0011YYYY [X]=[X] AND YYYY
70XXXXXX 0021YYYY [X]=[X] XOR YYYY
---Memory fill/copy---
40XXXXXX 2NUMSTEP 000000YY 000000ZZ byte[X+(0..NUM-1)*STEP*1]=Y+(0..NUM-1)*Z
40XXXXXX 1NUMSTEP 0000YYYY 0000ZZZZ half[X+(0..NUM-1)*STEP*2]=Y+(0..NUM-1)*Z
40XXXXXX 0NUMSTEP YYYYYYYY ZZZZZZZZ word[X+(0..NUM-1)*STEP*4]=Y+(0..NUM-1)*Z
50XXXXXX YYYYYYYY ZZZZZZZZ 00000000 copy Y bytes from [X] to [Z]
---Conditional codes (bugged)---
60XXXXXX 000000YY ZZZZZZZZ 01c100VV IF [[X]+Z] .. VV THEN [[X]+Z]=YY
60XXXXXX 000000YY ZZZZZZZZ 01c0VVVV IF [[X]+Z] .. VVVV THEN [[X]+Z]=YY
60XXXXXX 0000YYYY ZZZZZZZZ 11c100VV IF [[X]+Z] .. VV THEN [[X]+Z]=YYYY
60XXXXXX 0000YYYY ZZZZZZZZ 11c0VVVV IF [[X]+Z] .. VVVV THEN [[X]+Z]=YYYY
60XXXXXX YYYYYYYY ZZZZZZZZ 21c100VV IF [[X]+Z] .. VV THEN [[X]+Z]=YYYYYYYY
60XXXXXX YYYYYYYY ZZZZZZZZ 21c0VVVV IF [[X]+Z] .. VVVV THEN [[X]+Z]=YYYYYYYY
---Conditional codes (working)---
D0XXXXXX NNc100YY IF [X] .. YY THEN exec max(1,NN) lines
D0XXXXXX NNc0YYYY IF [X] .. YYYY THEN exec max(1,NN) lines
The condition digits (c=0..7), have the following functions:
0 IF [mem] = imm THEN ... 4 IF ([mem] AND imm) = 0 THEN ...
1 IF [mem] <> imm THEN ... 5 IF ([mem] AND imm) <> 0 THEN ...
2 IF [mem] < imm THEN ... (unsigned) 6 IF ([mem] AND imm) = imm THEN ...
3 IF [mem] > imm THEN ... (unsigned) 7 IF ([mem] AND imm) <> imm THEN ...
Notes
GAMECODE Cartridge Header[00Ch] (32bit in reversed byte-order)
CR16 Cartridge Header[15Eh] (16bit in normal byte-order)
XXXXXX 27bit addr (actually 7 digits, XXXXXXX, overlaps 5bit code number)
The “bugged” conditional codes (60XXXXXX) are accidently skipping NN lines when the condition is false, where NN is taken from the upper 8bit of the code’s last 32bit values (ie. exactly as for the D0XXXXXX codes). For byte-writes, that would be NN=01h, which can be eventually dealt with, although there may be compatibility problems which future versions that might fix that bug. For halfword/word writes, NN would be 11h or 21h, so that codes are about totally unusable.
The overall “address value” decryption works like so:
for i=4Fh to 00h
y=77628ECFh
if i>13h then y=59E5DC8Ah
if i>27h then y=054A7818h
if i>3Bh then y=B1BF0855h
address = (Key0-value) xor address
value = value - Key1 - (address ror 1Bh)
address = (address xor (value + y)) ror 13h
if (i>13h) then
if (i<=27h) or (i>3Bh) then x=Key2 xor Key1 xor Key0
else x=((Key2 xor Key1) and Key0) xor (Key1 and Key2)
value=value xor (x+y+address)
x = Secure[((i*4+00h) and FCh)+000h]
x = Secure[((i*4+34h) and FCh)+100h] xor x
x = Secure[((i*4+20h) and FCh)+200h] xor x
x = Secure[((i*4+08h) and FCh)+300h] xor x
address = address - (x ror 19h)
next i
Upon startup, the initial key settings are:
Secure[0..7FFh] = Copy of the ENCRYPTED 1st 2Kbytes of the game's Secure Area
Key0 = 0C2EAB3Eh, Key1 = E2AE295Dh, Key2 = E1ACC3FFh, Key3 = 70D3AF46h
scramble_keys
Upon BEEFC0DE XXXXXXXX, the keys get changed like so:
Key0 = Key0 + (XXXXXXXX ror 1Dh)
Key1 = Key1 - (XXXXXXXX ror 05h)
Key2 = Key2 xor (Key3 xor Key0)
Key3 = Key3 xor (Key2 - Key1)
scramble_keys
The above scramble_keys function works like so:
for i=0 to FFh
y = byte(xlat_table[i])
Secure[i*4+000h] = (Secure[i*4+000h] xor Secure[y*4]) + Secure[y*4+100h]
Secure[i*4+400h] = (Secure[i*4+400h] xor Secure[y*4]) - Secure[y*4+200h]
next i
for i=0 to 63h
Key0 = Key0 xor (Secure[i*4] + Secure[i*4+190h])
Key1 = Key1 xor (Secure[i*4] + Secure[i*4+320h])
Key2 = Key2 xor (Secure[i*4] + Secure[i*4+4B0h])
Key3 = Key3 xor (Secure[i*4] + Secure[i*4+640h])
next i
Key0 = Key0 - Secure[7D0h]
Key1 = Key1 xor Secure[7E0h]
Key2 = Key2 + Secure[7F0h]
Key3 = Key3 xor Secure[7D0h] xor Secure[7F0h]
the xlat_table consists of 256 fixed 8bit values:
34h,59h,00h,32h,7Bh,D3h,32h,C9h,9Bh,77h,75h,44h,E0h,73h,46h,06h
0Bh,88h,B3h,3Eh,ACh,F2h,BAh,FBh,2Bh,56h,FEh,7Ah,90h,F7h,8Dh,BCh
8Bh,86h,9Ch,89h,00h,19h,CDh,4Ch,54h,30h,01h,93h,30h,01h,FCh,36h
4Dh,9Fh,FDh,D7h,32h,94h,AEh,BCh,2Bh,61h,DFh,B3h,44h,EAh,8Bh,A3h
2Bh,53h,33h,54h,42h,27h,21h,DFh,A9h,DDh,C0h,35h,58h,EFh,8Bh,33h
B4h,D3h,1Bh,C7h,93h,AEh,32h,30h,F1h,CDh,A8h,8Ah,47h,8Ch,70h,0Ch
17h,4Eh,0Eh,A2h,85h,0Dh,6Eh,37h,4Ch,39h,1Fh,44h,98h,26h,D8h,A1h
B6h,54h,F3h,AFh,98h,83h,74h,0Eh,13h,6Eh,F4h,F7h,86h,80h,ECh,8Eh
EEh,4Ah,05h,A1h,F1h,EAh,B4h,D6h,B8h,65h,8Ah,39h,B3h,59h,11h,20h
B6h,BBh,4Dh,88h,68h,24h,12h,9Bh,59h,38h,06h,FAh,15h,1Dh,40h,F0h
01h,77h,57h,F5h,5Dh,76h,E5h,F1h,51h,7Dh,B4h,FAh,7Eh,D6h,32h,4Fh
0Eh,C8h,61h,C1h,EEh,FBh,2Ah,FCh,ABh,EAh,97h,D5h,5Dh,E8h,FAh,2Ch
06h,CCh,86h,D2h,8Ch,10h,D7h,4Ah,CEh,8Fh,EBh,03h,16h,ADh,84h,98h
F5h,88h,2Ah,18h,ACh,7Fh,F6h,94h,FBh,3Fh,00h,B6h,32h,A2h,ABh,28h
64h,5Ch,0Fh,C6h,23h,12h,0Ch,D2h,BAh,4Dh,A3h,F2h,C9h,86h,31h,57h
0Eh,F8h,ECh,E1h,A0h,9Ah,3Ch,65h,17h,18h,A0h,81h,D0h,DBh,D5h,AEh
all used operations are unsigned 32bit integer.
To Kenobi and Dualscreenman from Kodewerx for above CBDS cheat info.
DLDI (Dynamically Linked Device Interface for libfat) is a popular yet undocumented flashcart driver for homebrew NDS software dating back to 2006. Below was reverse-engineered 11/2018.
file.dldi –> driver file (can be small like 1.5Kbyte, or max 32Kbyte)
file.nds –> ROM image (must contain 32Kbyte space with DLDI ID for patching)
00h 4 DLDI ID (EDh,A5h,8Dh,BFh) (aka BF8DA5EDh) ;\patching tools will
04h 8 DLDI String (20h,"Chishm",00h) ; refuse any other
0Ch 1 DLDI Version (01h in .dldi, don't care in .nds) ;/values
0Dh 1 Size of .dldi+BSS (rounded up to 1 SHL N bytes) (max 0Fh=32Kbytes)
0Eh 1 Sections to fix/destroy (see FIX_xxx)
0Fh 1 Space in .nds file (1 SHL N) (0Eh..0Fh in .nds, can be 0 in .dldi)
10h 48 ASCII Full Driver Name (max 47 chars, plus zero padding)
40h 4 Address of ALL start (text) ;-base address (BF800000h in .dldi)
44h 4 Address of ALL end (data) ;-for highly-unstable FIX_ALL addr.adjusts
48h 4 Address of GLUE start ;\for semi-stable FIX_GLUE addr.adjusts
4Ch 4 Address of GLUE end ;/ ("Interworking glue" for ARM-vs-THUMB)
50h 4 Address of GOT start ;\for semi-stable FIX_GOT addr.adjusts
54h 4 Address of GOT end ;/ ("Global Offset Table")
58h 4 Address of BSS start ;\for zerofilling "BSS" via FIX_BSS
5Ch 4 Address of BSS end ;/ ("Block Started by Symbol")
60h 4 ASCII Short Driver/Device Name (4 chars, eg. "MYHW" for MyHardware)
64h 4 Flags 2 (see FEATURE_xxx) (usually 13h=GbaSlot, or 23h=NdsSlot)
68h 4 Address of Function startup() ;<-- must be at offset +80h !! ;\
6Ch 4 Address of Function isInserted() ;out: 0=no/fail, 1=yes/okay ; all
70h 4 Address of Function readSectors(sector,numSectors,buf) ; return
74h 4 Address of Function writeSectors(sector,numSectors,buf) ; 0=fail,
78h 4 Address of Function clearStatus() ; 1=okay
7Ch 4 Address of Function shutdown() ;/
80h .. Driver Code (can/must begin with "startup()") ;\max 7F80h
.. .. Glue section (usually a small snippet within above code) ; bytes (when
.. .. GOT section (usually after above code) (pointer table) ; having 32K
.. .. BSS section (usually at end, may exceed .dldi filesize) ; allocated)
.. .. Optional two garbage NOPs at end of default.dldi ;/
hdr[0Eh] - Sections to fix/destroy (FIX_xxx):
0 FIX_ALL ;-installer uses highly-unstable guessing in whole dldi file
1 FIX_GLUE ;-installer uses semi-stable address guessing in GLUE area
2 FIX_GOT ;-installer uses semi-stable address guessing in GOT area
3 FIX_BSS ;-installer will zerofill BSS area
4-7 Reserved (0)
hdr[64h] - Flags 2 (FEATURE_xxx) (usually 13h=GbaSlot, or 23h=NdsSlot):
0 FEATURE_MEDIUM_CANREAD 00000001h (usually set)
1 FEATURE_MEDIUM_CANWRITE 00000002h (a few carts can't write)
2-3 Reserved (0)
4 FEATURE_SLOT_GBA 00000010h (need EXMEMCNT bit7 adjusted)
5 FEATURE_SLOT_NDS 00000020h (need EXMEMCNT bit11 adjusted)
6-31 Reserved (0)
Note: The allocated driver size in hdr[0Fh] was 0Fh=32Kbytes between 2006 and 2016, however, libnds has changed that to 0Eh=16Kbytes in January 2017 (maybe intending to free more RAM, especially when using ARM7 WRAM).
However, there’s at least one driver exceeding 16K (rpg_nand.dldi in AKAIO package; the driver disguises itself as 8K driver in hdr[0Dh], but its BSS area actually needs ways MORE than 16K).
Officially, dldi could be at any 4-byte aligned location, however, for faster lookup, better use this locations:
dldi area should be located at a 40h-byte aligned address in ROM image.
dldi area should be located in ARM9 (or ARM7) bootcode area.
An “empty” driver needs to contain:
dldi[00h..0Bh] must contain DLDI ID word/string
dldi[0Fh] must contain allocated size (0Eh=16Kbyte or 0Fh=32Kbyte)
dldi[40h..43h] must contain RAM base address of DLDI block
and other entries should contain valid dummy strings and dummy functions.
An installed driver should contain a copy of the .dldi file, with addresses adjusted to RAM locations, and BSS area zerofilled (if FIX_BSS was set)
dldi[0Fh] must be kept as in the old .nds file (not as in .dldi file)
Some installers might try to detect homebrew by looking at nds carthdr for deciding whether or not to try to install dldi (unknown if/which ones are doing such things and looking at which carthdr entries).
startup, isInserted, clearStatus, shutdown can be dummy functions that do nothing (other than returning r0=1=okay).
Alternately startup/shutdown can initialize or power down the hardware, clearStatus is meant to be some sort of soft reset, and isInserted is allowing to test if the SD card is inserted & working.
read/write sectors are reading/writing one or more sectors. Sector size is 200h bytes, sector numbers is 0=First 200h bytes, 1=Next 200h bytes, and so on.
buf should be usually 4-byte aligned, however, some drivers do also support unaligned buffers using slower transfer code (better implement that when making .dldi drivers, but better don’t rely on it being supported when making .nds files).
The driver functions can support SD and SDHC (or the flashcart manufacturer might release driver updates if SDHC wasn’t supported).
Higher level FAT functions must be contained in the .nds file (so a driver update won’t help if the .nds file lacks FAT32) (and ExFAT most unlikely to be supported).
Functions should be ARM7 compatible, ie. don’t use BLX or POP r15 for mode switching, so the driver can be used on both ARM9 and ARM7 (or even on GBA).
SLOT_GBA/NDS seem to relate to GBA and NDS slots, the driver can probably have only one of the SLOT bits set (the functions don’t allow to select which slot to use).
Purpose is unclear to me, maybe just telling the .nds file that the flashcart is in the given slot (and thereby shouldn’t expect other hardware in that slot). Or maybe telling telling the installer which hardware the driver is supposed for.
FIX_xxx does maybe relate to address adjustments made by the dldi installer. Unknown if/how that’s working.
Some DLDI flashcarts support extra features like Rumble. However, that extra hardware is accessed via direct I/O, not via DLDI driver. Unknown which I/O ports are used for that stuff - probably something compatible with official GBA/NDS rumble cart(s).
The DLDI installer uses some guessing method for address-adjustments (the FIX_xxx flags are supposed to patch addresses, but not opcodes or other data).
The central mistake in the official DLDI installer is that it is patching all words at [start..end-1], using 1-byte address increments instead of 4-byte increments. This includes patching words at non-word aligned locations, or patching words whose lower bytes were already patched, and over-shooting to words at [end-1..end+2].
Most or all .dldi files are using ddmem base BF800000h (defined in dldi.ld). That value was chosen because it won’t conflict with opcodes (as NDS BIOS doesn’t use SWI function 80h, BF80xxxxh would be an invalid SWI function in ARM; and BF80h would be an invalid opcode in THUMB).
So far, this would have worked well, but it doesn’t work with unaligned-word patching bug (eg. THUMB opcodes 8000h,BF00h, or ARM opcodes xxBF80xxh or the like). And, even if it would have worked for opcodes - it might still fail for data values.
GOT does usually contain BF80xxxxh address pointers (plus some 00000000h words). The guessing works quite stable (the maximum for 32K files is xxxxh=7FFFh, so there’s no risk to encounter xxxx=BF80h) (one could encounter xxxxh=xxBFh, but the previously patched word is usually in RAM area, eg. 02xxxxxxh, so this would form BF02h, without risk to be seen as BF80h).
BUG: The GOT table is usually located at the end of the .dldi file, meaning that the over-shooting bug will see three uninitialized bytes at [got_end+0..2], and may go amok if they are BF80xxh or xxBF80h. The value of those bytes depends on left-over from previously installed .dldi driver(s) and on the ddmem base used in the .nds file, so the bug may take place randomly depending on several factors.
GLUE does usually contain a handful of opcodes and .pool values for switching between ARM and THUMB code. The intention is to patch the addresses in the .pool, and to leave the opcodes intact. This can be potentially stable, assuming that the used opcodes in the GLUE (and the next three bytes after glue_end) usually won’t contain BF80h).
This is the mother of all bugs. Fortunately there aren’t any .dldi drivers with FIX_ALL flag - and one should never make drivers that do use it.
ALL is covering the whole dldi space, including the 80h-byte DLDI header, the code area, including GLUE area, and GOT area, and probably also the yet uninitialized BSS area, and the next three bytes after end_all.
Patching the whole code area means an increased risk to hit opcodes or data values that contain BF80h. The over-shooting bug may even destroy the next three bytes after the 32Kbyte area.
Patching the DLDI header could destroy the header itself, the header in the .dldi file usually won’t contain BF80h at unintended locations, however, the pointers in that header are adjusted before applying FIX_ALL, for example, RAM base 0200BF00h (in .nds file) combined with a function at BF800080h (in .dldi file) would result in 0200BF80h. The nasty thing is that the problem won’t occur with other RAM base values (in other .nds files).
When making .dldi drivers: Never use FIX_ALL. And preferably avoid FIX_GOT and FIX_GLUE as well (ARM CPU can do relative jumps and relative addressing, eg. via ADR and ADRL pseudo opcodes, so there’s no point in needing address adjustments). Or otherwise append padding after GOT area, and try to avoid using opcodes/data with BF80h in/after GLUE area.
When making dldi installers: Best patch only word-aligned words (ARM CPU can’t access unaligned data, so there’s little chance that DLDI drivers would ever contain unaligned words). Or, when maintaining unaligned patching: At the very least skip the 80h-byte header on FIX_ALL, and after patching a word at other locations, skip the next three bytes, and don’t do the over-shooting at end.
When making .nds files: There isn’t too much that could be done here. One could set ddmem to 64Kbyte aligned addresses (so functions won’t end up at xxxxBF80h). Or one could even set ddmem to BF800000h (so patching will leave everything unchanged & intact - so one could then do the address adjustments manually, and hopefully more reliable than other DLDI installers).
The KEY1 encryption relies only on the gamecode (or firmware idcode), it does not contain any random components. The fact that KEY1 encrypted commands appear random is just because the <unencrypted> commands contain random values, so the encryption result looks random.
KEY1 encryption is used for KEY1 encrypted gamecart commands (ie. for loading the secure area). It is also used for resolving the extra decryption of the first 2K of the secure area, and for firmware decryption, and to decode some encrypted values in gamecart/firmware header.
Below formulas can be used only with a copy of the 1048h-byte key tables from NDS/DSi BIOS. The values can be found at:
NDS.ARM7 ROM: 00000030h..00001077h (values 99 D5 20 5F ..) Blowfish/NDS-mode
DSi.ARM9 ROM: FFFF99A0h..FFFFA9E7h (values 99 D5 20 5F ..) ""
DSi.TCM Copy: 01FFC894h..01FFD8DBh (values 99 D5 20 5F ..) ""
DSi.ARM7 ROM: 0000C6D0h..0000D717h (values 59 AA 56 8E ..) Blowfish/DSi-mode
DSi.RAM Copy: 03FFC654h..03FFD69Bh (values 59 AA 56 8E ..) ""
DSi.Debug: (stored in launcher) (values 69 63 52 05 ..) Blowfish/DSi-debug
The DSi ROM sections are disabled after booting, but the RAM/TCM copies can be dumped (eg. with some complex main memory hardware mods, or via unlaunch exploit). The DSi.Debug key is stored in launcher, and it’s used when SCFG_OP is nonzero (as so on debugging on hardware).
Y=[ptr+0]
X=[ptr+4]
FOR I=0 TO 0Fh (encrypt), or FOR I=11h TO 02h (decrypt)
Z=[keybuf+I*4] XOR X
X=[keybuf+048h+((Z SHR 24) AND FFh)*4]
X=[keybuf+448h+((Z SHR 16) AND FFh)*4] + X
X=[keybuf+848h+((Z SHR 8) AND FFh)*4] XOR X
X=[keybuf+C48h+((Z SHR 0) AND FFh)*4] + X
X=Y XOR X
Y=Z
NEXT I
[ptr+0]=X XOR [keybuf+40h] (encrypt), or [ptr+0]=X XOR [keybuf+4h] (decrypt)
[ptr+4]=Y XOR [keybuf+44h] (encrypt), or [ptr+4]=Y XOR [keybuf+0h] (decrypt)
encrypt_64bit(keycode+4)
encrypt_64bit(keycode+0)
[scratch]=0000000000000000h ;S=0 (64bit)
FOR I=0 TO 44h STEP 4 ;xor with reversed byte-order (bswap)
[keybuf+I]=[keybuf+I] XOR bswap_32bit([keycode+(I MOD modulo)])
NEXT I
FOR I=0 TO 1040h STEP 8
encrypt_64bit(scratch) ;encrypt S (64bit) by keybuf
[keybuf+I+0]=[scratch+4] ;write S to keybuf (first upper 32bit)
[keybuf+I+4]=[scratch+0] ;write S to keybuf (then lower 32bit)
NEXT I
if key=nds then copy [nds_arm7bios+0030h..1077h] to [keybuf+0..1047h]
if key=dsi then copy [dsi_arm7bios+C6D0h..D717h] to [keybuf+0..1047h]
[keycode+0]=[idcode]
[keycode+4]=[idcode]/2
[keycode+8]=[idcode]*2
IF level>=1 THEN apply_keycode(modulo) ;first apply (always)
IF level>=2 THEN apply_keycode(modulo) ;second apply (optional)
[keycode+4]=[keycode+4]*2
[keycode+8]=[keycode+8]/2
IF level>=3 THEN apply_keycode(modulo) ;third apply (optional)
init_keycode(firmware_header+08h,1,0Ch,nds) ;idcode (usually "MACP"), level 1
decrypt_64bit(firmware_header+18h) ;rominfo
init_keycode(firmware_header+08h,2,0Ch,nds) ;idcode (usually "MACP"), level 2
decrypt ARM9 and ARM7 bootcode by decrypt_64bit (each 8 bytes)
decompress ARM9 and ARM7 bootcode by LZ77 function (swi)
calc CRC16 on decrypted/decompressed ARM9 bootcode followed by ARM7 bootcode
Note: The sizes of the compressed/encrypted bootcode areas are unknown (until they are fully decompressed), one way to solve that problem is to decrypt the next 8 bytes each time when the decompression function requires more data.
init_keycode(cart_header+0Ch,1,08h,nds) ;gamecode, level 1, modulo 8
decrypt_64bit(cart_header+78h) ;rominfo (secure area disable)
init_keycode(cart_header+0Ch,2,08h,nds) ;gamecode, level 2, modulo 8
encrypt_64bit all NDS KEY1 commands (1st command byte in MSB of 64bit value)
after loading the secure_area, calculate secure_area crc, then
decrypt_64bit(secure_area+0) ;first 8 bytes of secure area
init_keycode(cart_header+0Ch,3,08h,nds) ;gamecode, level 3, modulo 8
decrypt_64bit(secure_area+0..7F8h) ;each 8 bytes in first 2K of secure
init_keycode(cart_header+0Ch,1,08h,dsi) ;gamecode, level 1, modulo 8
encrypt_64bit all DSi KEY1 commands (1st command byte in MSB of 64bit value)
After secure area decryption, the ID field in the first 8 bytes should be “encryObj”, if it matches then first 8 bytes are filled with E7FFDEFFh, otherwise the whole 2K are filled by that value.
Observe that the byte-order of the command register [40001A8h] is reversed. The way how the CPU stores 64bit data in memory (and the way how the “encrypt_64bit” function for KEY1-encrypted commands expects data in memory) is LSB at [addr+0] and MSB at [addr+7]. This value is to be transferred MSB first. However, the DS hardware transfers [40001A8h+0] first, and [40001A8h+7] last. So, the byte order must be reversed when copying the value from memory to the command register.
The KEY1 encryption is based on Bruce Schneier’s “Blowfish Encryption Algorithm”.
The pre-initialization settings at cartridge-side (after reset) are:
Seed0 = 58C56DE0E8h
Seed1 = 5C879B9B05h
The post-initialization settings (after sending command 4llllmmmnnnkkkkkh to the cartridge, and after writing the Seed values to Port 40001Bxh) are:
Seed0 = (mmmnnn SHL 15)+6000h+Seedbyte
Seed1 = 5C879B9B05h
The seedbyte is selected by Cartridge Header [013h].Bit0-2, this index value should be usually in range 0..5, however, possible values for index 0..7 are: E8h,4Dh,5Ah,B1h,17h,8Fh,99h,D5h.
The 24bit random value (mmmnnn) is derived from the real time clock setting, and also scattered by KEY1 encryption, anyways, it’s just random and doesn’t really matter where it comes from.
Relies on two 39bit registers (x and y), which are initialized as such:
x = reversed_bit_order(seed0) ;ie. LSB(bit0) exchanged with MSB(bit38), etc.
y = reversed_bit_order(seed1)
During transfer, x, y, and transferred data are modified as such:
x = (((x shr 5)xor(x shr 17)xor(x shr 18)xor(x shr 31)) and 0FFh)+(x shl 8)
y = (((y shr 5)xor(y shr 23)xor(y shr 18)xor(y shr 31)) and 0FFh)+(y shl 8)
data = (data xor x xor y) and 0FFh
FLASH has more than 100,000 Write Cycles, more than 20 Year Data Retention
The Firmware Flash Memory is accessed via SPI bus,
06h WREN Write Enable (No Parameters)
04h WRDI Write Disable (No Parameters)
9Fh RDID Read JEDEC Identification (Read 1..3 ID Bytes)
(Manufacturer, Device Type, Capacity)
05h RDSR Read Status Register (Read Status Register, endless repeated)
Bit7-2 Not used (zero)
Bit1 WEL Write Enable Latch (0=No, 1=Enable)
Bit0 WIP Write/Program/Erase in Progess (0=No, 1=Busy)
03h READ Read Data Bytes (Write 3-Byte-Address, read endless data stream)
0Bh FAST Read Data Bytes at Higher Speed (Write 3-Byte-Address, write 1
dummy-byte, read endless data stream) (max 25Mbit/s)
0Ah PW Page Write (Write 3-Byte-Address, write 1..256 data bytes)
(changing bits to 0 or 1) (reads unchanged data, erases the page,
then writes new & unchanged data) (11ms typ, 25ms max)
02h PP Page Program (Write 3-Byte-Address, write 1..256 data bytes)
(changing bits from 1 to 0) (1.2ms typ, 5ms max)
DBh PE Page Erase 100h bytes (Write 3-Byte-Address) (10ms typ, 20ms max)
D8h SE Sector Erase 10000h bytes (Write 3-Byte-Address) (1s typ, 5s max)
B9h DP Deep Power-down (No Parameters) (consumption 1uA typ, 10uA max)
(3us) (ignores all further instructions, except RDP)
ABh RDP Release from Deep Power-down (No Parameters) (30us)
Write/Program may not cross page-boundaries. Write/Program/Erase are rejected during first 1..10ms after power up. The WEL bit is automatically cleared on Power-Up, on /Reset, and on completion of WRDI/PW/PP/PE/SE instructions. WEL is set by WREN instruction (which must be issued before any write/program/erase instructions). Don’t know how RDSR behaves when trying to write to the write-protected region?
Set Chip Select LOW to invoke the command
Transmit the instruction byte
Transmit any parameter bytes
Transmit/receive any data bytes
Set Chip Select HIGH to finish the command
All bytes (and 3-byte addresses) transferred most significant bit/byte first.
Early DSi DWM-W015 boards did have 128Kbyte FLASH, but later boards have custum 4Kbyte FLASH chips (these 4K chips are found on later DSi DWM-W015 boards, and DSi DWM-W024 boards, and 3DS DWM-W028 boards). The chips are having a 24bit address bus (needed for NDS compatibility), and, a weird non-writeable gap within a 128Kbyte memory are:
000000h..0002FFh Writeable only if /WP=HIGH (otherwise writes are ignored)
000300h..01F2FFh Not writeable (FFh-filled, writes are ignored)
01F300h..01FFFFh Writeable
020000h and up Mirrors of 0..01FFFFh (same read/write-ability as above)
There are several part numbers: “5A32” (DSi), “5K32” (3DS), “32B, 3XH” (3DS), and “26FV032T” (DSi), that chips are probably all the same size & functionally same; most of those 4Kbyte chips have tiny packages (except “26FV032T” which comes in classic large package).
1 D Serial Data In (latched at rising clock edge) _________
2 C Serial Clock (max 25MHz) /|o |
3 /RES Reset 1 -| | |- 8
4 /S Chip Select (instructions start at falling edge) 2 -| | |- 7
5 /W Write Protect (makes first 256 pages read-only) 3 -| |_________|- 6
6 VCC Supply (2.7V..3.6V typ) (4V max) (DS:VDD3.3) 4 -|/ |- 5
7 VSS Ground |___________|
8 Q Serial Data Out (changes at falling clock edge)
1 /S Chip Select (instructions start at falling edge) ___________
2 Q Serial Data Out (changes at falling clock edge) 1 -| o |- 8
3 /W Write Protect (makes first pages read-only) 2 -| |- 7
4 VSS Ground 3 -| |- 6
5 D Serial Data In (latched at rising clock edge) 4 -|___________|- 5
6 C Serial Clock
7 /RES Reset
8 VCC Supply (2.7V..3.6V typ) (DSi: VDD33)
00000h-00029h Firmware Header
0002Ah-001FFh Wifi Settings
00200h-3F9FFh Firmware Code/Data ;-NDS only (not DSi)
00200h-002FEh 00h-filled ;\
002FFh 80h ;
00300h-1F2FFh FFh-filled (not write-able on 4K chips) ; DSi only (not NDS)
1F300h-1F3FEh FFh-filled (write-able) ;
1F3FFh Whatever Debug/Bootflags ;
1F400h-1F5FFh Wifi Access Point 4 ;\with WPA/WPA2 ;
1F600h-1F7FFh Wifi Access Point 5 ; support ;
1F800h-1F9FFh Wifi Access Point 6 ;/ ;/
3FA00h-3FAFFh Wifi Access Point 1 ;\
3FB00h-3FBFFh Wifi Access Point 2 ; Open/WEP only
3FC00h-3FCFFh Wifi Access Point 3 ;/
3FD00h-3FDFFh Not used
3FE00h-3FEFFh User Settings Area 1
3FF00h-3FFFFh User Settings Area 2
On iQue DS (with 512K flash memory), user settings are moved to 7FE00h and up, and, there seems to be some unknown stuff at 200h..27Fh.
Addr Size Expl.
000h 2 part3 romaddr/8 (arm9 gui code) (LZ/huffman compression)
002h 2 part4 romaddr/8 (arm7 wifi code) (LZ/huffman compression)
004h 2 part3/4 CRC16 arm9/7 gui/wifi code
006h 2 part1/2 CRC16 arm9/7 boot code
008h 4 firmware identifier (usually nintendo "MAC",nn) (or nocash "XBOO")
the 4th byte (nn) occassionally changes in different versions
00Ch 2 part1 arm9 boot code romaddr/2^(2+shift1) (LZSS compressed)
00Eh 2 part1 arm9 boot code 2800000h-ramaddr/2^(2+shift2)
010h 2 part2 arm7 boot code romaddr/2^(2+shift3) (LZSS compressed)
012h 2 part2 arm7 boot code 3810000h-ramaddr/2^(2+shift4)
014h 2 shift amounts, bit0-2=shift1, bit3-5=shift2, bit6-8=shift3,
bit9-11=shift4, bit12-15=firmware_chipsize/128K
016h 2 part5 data/gfx romaddr/8 (LZ/huffman compression)
018h 8 Optional KEY1-encrypted "enPngOFF"=Cartridge KEY2 Disable
(feature isn't used in any consoles, instead contains timestamp)
018h 5 Firmware version built timestamp (BCD minute,hour,day,month,year)
01Dh 1 Console type
FFh=Nintendo DS
20h=Nintendo DS-lite
57h=Nintendo DSi (also iQueDSi)
43h=iQueDS
63h=iQueDS-lite
The entry was unused (FFh) in older NDS, ie. replace FFh by 00h)
Bit0 seems to be DSi/iQue related
Bit1 seems to be DSi/iQue related
Bit2 seems to be DSi related
Bit3 zero
Bit4 seems to be DSi related
Bit5 seems to be DS-Lite related
Bit6 indicates presence of "extended" user settings (DSi/iQue)
Bit7 zero
01Eh 2 Unused (FFh-filled)
020h 2 User Settings Offset (div8) (usually last 200h flash bytes)
022h 2 Unknown (7EC0h or 0B51h)
024h 2 Unknown (7E40h or 0DB3h)
026h 2 part5 CRC16 data/gfx
028h 2 unused (FFh-filled)
02Ah-1FFh Wifi Calibration Data (see next chapter)
000h 1Dh Zerofilled (bootcode is in new eMMC chip, not on old FLASH chip)
01Dh 6 Same as on DS (header: Console Type and User Settings Offset)
022h 6 Zerofilled (bootcode is in new eMMC chip, not on old FLASH chip)
028h..1FCh Same as on DS (wifi calibration)
1FDh 1 Wifi Board (01h=DWM-W015, 02h=W024, 03h=W028) ;\this was
1FEh 1 Wifi Flash (20h=With access point 4/5/6) ; FFh-filled
1FFh 1 Same as on DS (FFh) ;/on DS
200h FFh Zerofilled ;\
2FFh 1 Unknown (80h) ; this was
300h 1F000h FFh's (not write-able on 4K chips) ; bootcode
1F300h FFh FFh's (write-able) ;twl-debugger: 00h's ; on DS
1F3FFh 1 FFh ;twl-debugger: 40h ;/
The bytes [000h..027h] cannot be changed on DSi because they are part of the RSA signature in DSi’s Boot Info Block (at eMMC offset 200h..3FFh).
Addr Size Expl.
000h-029h Firmware Header (see previous chapter)
02Ah 2 CRC16 (with initial value 0) of [2Ch..2Ch+config_length-1]
02Ch 2 config_length (usually 0138h, ie. entries 2Ch..163h)
02Eh 1 Unused (00h)
02Fh 1 Version (0=v1..v4, 3=v5, 5=v6..v7,6=W006,15=W015,24=W024,34=N3DS)
030h 6 Unused (00h-filled) (DS-Lite and DSi: FF,FF,FF,FF,FF,00)
036h 6 48bit MAC address (v1-v5: 0009BFxxxxxx, v6-v7: 001656xxxxxx)
03Ch 2 list of enabled channels ANDed with 7FFE (Bit1..14 = Channel 1..14)
(usually 3FFEh, ie. only channel 1..13 enabled)
03Eh 2 Whatever Flags (usually FFFFh)
040h 1 RF Chip Type (NDS: usually 02h) (DS-Lite and DSi/3DS: usually 03h)
041h 1 RF Bits per entry at 0CEh (usually 18h=24bit=3byte) (Bit7=?)
042h 1 RF Number of entries at 0CEh (usually 0Ch)
043h 1 Unknown (usually 01h)
044h 2 Initial Value for [4808146h] ;W_CONFIG_146h
046h 2 Initial Value for [4808148h] ;W_CONFIG_148h
048h 2 Initial Value for [480814Ah] ;W_CONFIG_14Ah
04Ah 2 Initial Value for [480814Ch] ;W_CONFIG_14Ch
04Ch 2 Initial Value for [4808120h] ;W_CONFIG_120h
04Eh 2 Initial Value for [4808122h] ;W_CONFIG_122h
050h 2 Initial Value for [4808154h] ;W_CONFIG_154h
052h 2 Initial Value for [4808144h] ;W_CONFIG_144h
054h 2 Initial Value for [4808130h] ;W_CONFIG_130h
056h 2 Initial Value for [4808132h] ;W_CONFIG_132h
058h 2 Initial Value for [4808140h] ;W_CONFIG_140h ;maybe ACK timeout?
05Ah 2 Initial Value for [4808142h] ;W_CONFIG_142h
05Ch 2 Initial Value for [4808038h] ;W_POWER_TX
05Eh 2 Initial Value for [4808124h] ;W_CONFIG_124h
060h 2 Initial Value for [4808128h] ;W_CONFIG_128h
062h 2 Initial Value for [4808150h] ;W_CONFIG_150h
064h 69h Initial 8bit values for BB[0..68h]
0CDh 1 Unused (00h)
Below for Type2 (ie. when [040h]=2) (Mitsumi MM3155 and RF9008):
0CEh 24h Initial 24bit values for RF[0,4,5,6,7,8,9,0Ah,0Bh,1,2,3]
0F2h 54h Channel 1..14 2x24bit values for RF[5,6]
146h 0Eh Channel 1..14 8bit values for BB[1Eh] (usually somewhat B1h..B7h)
154h 0Eh Channel 1..14 8bit values for RF[9].Bit10..14 (usually 10h-filled)
Below for Type3 (ie. when [040h]=3) (Mitsumi MM3218) (and AR6013G):
--- Type3 values are originated at 0CEh, following addresses depend on: ---
1) number of initial values, found at [042h] ;usually 29h
2) number of BB indices, found at [0CEh+[042h]] ;usually 02h
3) number of RF indices, found at [043h] ;usually 02h
--- Below example addresses assume above values to be set to 29h,02h,02h ---
0CEh 29h Initial 8bit values for RF[0..28h]
0F7h 1 Number of BB indices per channel
0F8h 1 1st BB index
0F9h 14 1st BB data for channel 1..14
107h 1 2nd BB index
108h 14 2nd BB data for channel 1..14
116h 1 1st RF index
117h 14 1st RF data for channel 1..14
125h 1 2nd RF index
126h 14 2nd RF data for channel 1..14
134h 46 Unused (FFh-filled)
Below for both Type2 and Type3:
162h 1 Unknown (usually 19h..1Ch)
163h 1 Unused (FFh) (Inside CRC16 region, with config_length=138h)
164h 99h Unused (FFh-filled) (Outside CRC16 region, with config_length=138h)
1FDh 1 DSi/3DS Wifi Board (01h=W015, 02h=W024, 03h=W028);\this was
1FEh 1 DSi/3DS Wifi Flash (20h=With access point 4/5/6) ; FFh-filled on DS
1FFh 1 DSi/3DS Same as on DS (FFh) ;/
Most of the Wifi settings seem to be always the same values on all currently existing consoles. Except for:
Values that are (obviously) different are the CRC16, and 4th-6th bytes of the MAC address. Also, initial values for BB[01h] and BB[1Eh], and channel 1..14 values for BB[1Eh], and unknown entry [162h] contain different calibration settings on all consoles.
Firmware v5 is having a new wifi ID [2Fh]=03h, and different RF[9] setting.
Firmware v6 (dslite) has wifi ID [2Fh]=05h, and same RF[9] setting as v5, additionally, v6 and up have different 2nd-3rd bytes of the MAC address.
Moreover, a LOT of values are different with Type3 chips (ie. when [040h]=3).
Unlike for Firmware User Settings, the Firmware Header (and Wifi Settings) aren’t stored in RAM upon boot. So the data must be retrieved via SPI bus by software.
These three 100h byte regions are used to memorize known internet access points. The NDS firmware doesn’t use these regions, but games that support internet are allowed to read (and configure/write) them. The DSi firmware also supports configuring these entries.
Addr Siz Expl.
000h 64 Unknown (usually 00h-filled) (no Proxy supported on NDS)
040h 32 SSID (ASCII name of the access point) (padded with 00h's)
060h 32 SSID for WEP64 on AOSS router (each security level has its own SSID)
080h 16 WEP Key 1 (for type/size, see entry E6h)
090h 16 WEP Key 2 ;\
0A0h 16 WEP Key 3 ; (usually 00h-filled)
0B0h 16 WEP Key 4 ;/
0C0h 4 IP Address (0=Auto/DHCP)
0C4h 4 Gateway (0=Auto/DHCP)
0C8h 4 Primary DNS Server (0=Auto/DHCP)
0CCh 4 Secondary DNS Server (0=Auto/DHCP)
0D0h 1 Subnet Mask (0=Auto/DHCP, 1..1Ch=Leading Ones) (eg. 6 = FC.00.00.00)
0D1h .. Unknown (usually 00h-filled)
0E6h 1 WEP Mode (0=None, 1/2/3=5/13/16 byte hex, 5/6/7=5/13/16 byte ascii)
0E7h 1 Status (00h=Normal, 01h=AOSS, FFh=connection not configured/deleted)
0E8h 1 Zero (not SSID Length, ie. unlike as entry 4,5,6 on DSi)
0E9h 1 Unknown (usually 00h)
0EAh 2 DSi only: MTU (Max transmission unit) (576..1500, usually 1400)
0ECh 3 Unknown (usually 00h-filled)
0EFh 1 bit0/1/2 - connection 1/2/3 (1=Configured, 0=Not configured)
0F0h 6 Nintendo Wifi Connection (WFC) 43bit User ID
(ID=([F0h] AND 07FFFFFFFFFFh)*1000, shown as decimal string
NNNN-NNNN-NNNN-N000) (the upper 5bit of the last byte are
containing additional/unknown nonzero data)
0F6h 8 Unknown (nonzero stuff !?!)
0FEh 2 CRC16 for Entries 000h..0FDh (with initial value 0000h)
For connection 3: entries [0EFh..0FDh] - always zero-filled?
The location of the first data block is at the User Settings address minus 400h, ie. Firmware Header [00020h]*8-400h.
The DSi has three extra 200h-byte regions (for use DSi games, with the new WPA/WPA2 encryption support, and with additional proxy support), these extra regions are found under “Advanced Setup” in the DSi firmware’s “Internet” configuration menu.
Addr Siz Expl.
000h 32 Proxy Authentication Username (ASCII string, padded with 00's)
000h 32 Proxy Authentication Password (ASCII string, padded with 00's)
040h 32 SSID (ASCII string, padded with 00's) (see [0E8h] for length)
060h .. Maybe same as NDS
080h 16 WEP Key (zerofilled for WPA)
0xxh .. Maybe same as NDS
0C0h 4 IP Address (0=Auto/DHCP)
0C4h 4 Gateway (0=Auto/DHCP)
0C8h 4 Primary DNS Server (0=Auto/DHCP)
0CCh 4 Secondary DNS Server (0=Auto/DHCP)
0D0h 1 Subnet Mask (0=Auto/DHCP, 1..1Ch=Leading Ones) (eg. 6 = FC.00.00.00)
0D1h .. Unknown (zerofilled)
0E6h 1 WEP (00h=None/WPA/WPA2, 01h/02h/03h/05h/06h/07h=WEP, same as NDS)
0E7h 1 WPA (00h=Normal, 10h=WPA/WPA2, 13h=WPS+WPA/WPA2, FFh=unused/deleted)
0E8h 1 SSID Length in characters (01h..20h, or 00h=unused)
0E9h 1 Unknown (usually 00h)
0EAh 2 MTU Value (Max transmission unit) (576..1500, usually 1400)
0ECh 3 Unknown (usually 00h-filled)
0EFh 1 bit0/1/2 - connection 4/5/6 (1=Configured, 0=Not configured)
0F0h 14 Zerofilled (or maybe ID as on NDS, if any such ID exists for DSi?)
0FEh 2 CRC16 for Entries 000h..0FDh (with initial value 0000h)
100h 32 Precomputed PSK (based on WPA/WPA2 password and SSID) ;\all zero
120h 64 WPA/WPA2 password (ASCII string, padded with 00's) ;/for WEP
160h 33 Zerofilled
181h 1 WPA (0=None/WEP, 4=WPA-TKIP, 5=WPA2-TKIP, 6=WPA-AES, 7=WPA2-AES)
182h 1 Proxy Enable (00h=None, 01h=Yes)
183h 1 Proxy Authentication (00h=None, 01h=Yes)
184h 48 Proxy Name (ASCII string, max 47 chars, padded with 00's)
1B4h 52 Zerofilled
1E8h 2 Proxy Port (16bit)
1EAh 20 Zerofilled
1FEh 2 CRC16 for Entries 100h..1FDh (with initial value 0000h) (0=deleted)
The location of the first data block (aka settings number 4) is at the User Settings address minus A00h, ie. Firmware Header [00020h]*8-A00h.
Observe that NDS consoles do have NDS Firmware bootcode/data in that area, so those new regions can exist on DSi only (or on homebrew NDS firmwares). Presence of the new regions is indicated in Firmware Header [001FEh], that byte is usually FFh=NDS or 20h=DSi, the DSi browser does internally replace FFh by 10h, and does then check if byte>=20h (ie. the new areas exist if the byte is 20h..FEh).
Note that the Proxy feature can be used to redirect internet access (when using a custom proxy server, one could redirect commercial games to homebrew servers; as done by the
The location of the user settings & connection data varies (eg. 01Fxxxh=DSi, 03Fxxxh=NDS, 07Fxxxh=iQueDS).
DSi games and DSi browser can reportedly also connect to Nintendo’s public access points (those that are announced via Nintendo Zone Beacons).
This seems to hold an extra 100h-byte region (same as Connection data 1-3), some (or all) NDS/DSi games seem to be capable of using it (although it isn’t shown in config menues). One theory is that it might be used for the Nintendo Wi-Fi USB Connector?
Addr Size Expl.
000h 2 Version (5) (Always 5, for all NDS/DSi Firmware versions)
002h 1 Favorite color (0..15) (0=Gray, 1=Brown, etc.)
003h 1 Birthday month (1..12) (Binary, non-BCD)
004h 1 Birthday day (1..31) (Binary, non-BCD)
005h 1 Not used (zero)
006h 20 Nickname string in UTF-16 format
01Ah 2 Nickname length in characters (0..10)
01Ch 52 Message string in UTF-16 format
050h 2 Message length in characters (0..26)
052h 1 Alarm hour (0..23) (Binary, non-BCD)
053h 1 Alarm minute (0..59) (Binary, non-BCD)
054h 2
056h 1 80h=enable alarm (huh?), bit 0..6=enable?
057h 1 Zero (1 byte)
058h 2x2 Touch-screen calibration point (adc.x1,y1) 12bit ADC-position
05Ch 2x1 Touch-screen calibration point (scr.x1,y1) 8bit pixel-position
05Eh 2x2 Touch-screen calibration point (adc.x2,y2) 12bit ADC-position
062h 2x1 Touch-screen calibration point (scr.x2,y2) 8bit pixel-position
064h 2 Language and Flags (see below)
066h 1 Year (2000..2255) (when having entered date in the boot menu)
067h 1 Unknown (usually 00h...08h or 78h..7Fh or so)
068h 4 RTC Offset (difference in seconds when RTC time/date was changed)
06Ch 4 Not used (FFh-filled, sometimes 00h-filled) (=MSBs of above?)
Below not stored in RAM (found only in FLASH memory)…
070h 2 Update counter (used to check latest) (must be 0000h..007Fh)
072h 2 CRC16 of entries 00h..6Fh (70h bytes)
074h 8Ch Not used (FFh-filled) (or extended data, see below)
Below extended data was invented for iQue DS (for adding the chinese language setting), and is also included in Nintendo DSi models. Presence of extended data is indicated in Firmware Header entry [1Dh].Bit6.
074h 1 Unknown (01h) (maybe version?)
075h 1 Extended Language (0..5=Same as Entry 064h, plus 6=Chinese)
(for language 6, entry 064h defaults to english; for compatibility)
(for language 0..5, both entries 064h and 075h have same value)
076h 2 Bitmask for Supported Languages (Bit0..6)
(007Eh for iQue DS, ie. with chinese, but without japanese)
(0042h for iQue DSi, chinese (and english, but only for NDS mode))
(003Eh for DSi/EUR, ie. without chinese, and without japanese)
078h 86h Not used (FFh-filled on iQue DS, 00h-filled on DSi)
0FEh 2 CRC16 of entries 74h..FDh (8Ah bytes)
Note: The DSi has some more settings (eg. Country (additionally to Language), Parental Controls, and a surreal fake Wireless-Disable option; which does only disable the Wifi LED, the actual Wifi transmission does still work).
That new settings are stored in eMMC files. The old/above User Settings are stored in those files too (and copy of those User Settings is stored in Wifi FLASH, as described above; that copy is intended mainly for backwards compatibilty with NDS games).
0..2 Language (0=Japanese, 1=English, 2=French, 3=German,
4=Italian, 5=Spanish, 6..7=Reserved) (for Chinese see Entry 075h)
(the language setting also implies time/data format)
3 GBA mode screen selection (0=Upper, 1=Lower)
4-5 Backlight Level (0..3=Low,Med,High,Max) (DS-Lite only)
6 Bootmenu Disable (0=Manual/bootmenu, 1=Autostart Cartridge)
7-8 ?
9 Settings Lost (1=Prompt for User Info, and Language, and Calibration)
10 Settings Okay (0=Prompt for User Info)
11 Settings Okay (0=Prompt for User Info) (Same as Bit10)
12 No function
13 Settings Okay (0=Prompt for User Info, and Language)
14 Settings Okay (0=Prompt for User Info) (Same as Bit10)
15 Settings Okay (0=Prompt for User Info) (Same as Bit10)
The Health and Safety message is skipped if Bit9=1, or if one or more of the following bits is zero: Bits 10,11,13,14,15. However, as soon as entering the bootmenu, the Penalty-Prompt occurs.
Note: There are two User Settings areas in the firmware chip, at offset 3FE00h and 3FF00h, if both areas have valid CRCs, then the current/newest area is that whose Update Counter is one bigger than in the other/older area.
IF count1=((count0+1) AND 7Fh) THEN area1=newer ELSE area0=newer
When changing settings, the older area is overwritten with new data (and incremented Update Counter). The two areas allow to recover previous settings in case of a write-error (eg. on a battery failure during write).
Even though the battery is required only for the RTC (not for the firmware flash memory), most of the firmware user settings are reset when removing the battery. This appears to be a strange bug-or-feature of the DS bios, at least, fortunately, it still keeps the rest of the firmware intact.
Extended Settings contain some additional information which is not supported by the original firmware (current century, date/time formats, temperature calibration, etc.), the settings are supported by Nocash Firmware, by the no$gba emulator, and may be eventually also supported by other emulators. If present, the values can be used by games, otherwise games should use either whatever default settings, or contain their own configuration menu.
Addr Siz Expl.
00h 8 ID "XbooInfo"
08h 2 CRC16 Value [0Ch..0Ch+Length-1]
0Ah 2 CRC16 Length (from 0Ch and up)
0Ch 1 Version (currently 01h)
0Dh 1 Update Count (newer = (older+1) AND FFh)
0Eh 1 Bootmenu Flags
Bit6 Important Info (0=Disable, 1=Enable)
Bit7 Bootmenu Screen (0=Upper, 1=Lower)
0Fh 1 GBA Border (0=Black, 1=Gray Line)
10h 2 Temperature Calibration TP0 ADC value (x16) (sum of 16 ADC values)
12h 2 Temperature Calibration TP1 ADC value (x16) (sum of 16 ADC values)
14h 2 Temperature Calibration Degrees Kelvin (x100) (0=none)
16h 1 Temperature Flags
Bit0-1 Format (0=Celsius, 1=Fahrenheit, 2=Reaumur, 3=Kelvin)
17h 1 Backlight Intensity (0=0ff .. FFh=Full)
18h 4 Date Century Offset (currently 20, for years 2000..2099)
1Ch 1 Date Month Recovery Value (1..12)
1Dh 1 Date Day Recovery Value (1..31)
1Eh 1 Date Year Recovery Value (0..99)
1Fh 1 Date/Time Flags
Bit0-1 Date Format (0=YYYY-MM-DD, 1=MM-DD-YYYY, 2=DD-MM-YYYY)
Bit2 Friendly Date (0=Raw Numeric, 1=With Day/Month Names)
Bit5 Time DST (0=Hide DST, 1=Show DST=On/Off)
Bit6 Time Seconds (0=Hide Seconds, 1=Show Seconds)
Bit7 Time Format (0=24 hour, 1=12 hour)
20h 1 Date Separator (Ascii, usually Slash, or Dot)
21h 1 Time Separator (Ascii, usually Colon, or Dot)
22h 1 Decimal Separator (Ascii, usually Comma, or Dot)
23h 1 Thousands Separator (Ascii, usually Comma, or Dot)
24h 1 Daylight Saving Time (Nth)
Bit 0-3 Activate on (0..4 = Last,1st,2nd,3rd,4th)
Bit 4-7 Deactivate on (0..4 = Last,1st,2nd,3rd,4th)
25h 1 Daylight Saving Time (Day)
Bit 0-3 Activate on (0..7 = Mon,Tue,Wed,Thu,Fri,Sat,Sun,AnyDay)
Bit 4-7 Deactivate on (0..7 = Mon,Tue,Wed,Thu,Fri,Sat,Sun,AnyDay)
26h 1 Daylight Saving Time (of Month)
Bit 0-3 Activate DST in Month (1..12)
Bit 4-7 Deactivate DST in Month (1..12)
27h 1 Daylight Saving Time (Flags)
Bit 0 Current DST State (0=Off, 1=On)
Bit 1 Adjust DST Enable (0=Disable, 1=Enable)
Note: With the original firmware, the memory region at 23FEE00h and up contains un-initialized, non-zero-filled data (fragments of boot code).
MESG files contain localized strings (like “Okay” and “Cancel” and longer text messages). Different languages are stored separate message files, usually with .bmg extension. MESG files are used in NDS/DSi titles (in little endian), and also on Wii (in big endian).
DSi titles are: DSi Shop, Launcher, System Settings, and DSi Camera (however, the DSi-to-3DS Transfer Tool uses the newer MsgPrjBn/MsgStdBn format; despite of being a DSi program).
000h 8 ID "MESGbmg1" ;or "GSEM1gmb" in Super Mario 64 DS
008h 4 Total Filesize ;or Filesize+1 in Super Mario 64 DS
00Ch 4 Number of Chunks (2=INF1+DAT1, 3=INF1+DAT1+MID1)
010h 1 Encoding (1=CP1252, 2=UTF-16, 3=Shift-JIS, 4=UTF-8)
011h 15 Padding (0)
Encoding UTF-16 appears to be most common (Super Mario 64 DS uses Shift-JIS).
It comes just after the BMG header. It contains information (like pointers) about the messages.
000h 4 Chunk ID "INF1" ;or "1FNI" in Super Mario 64 DS
004h 4 Chunk Size
008h 2 Number of messages (N)
00Ah 2 Size of each INF data in bytes ;or in BITs in Super Mario 64 DS
00Ch 4 "BMG file ID = ID for this BMG file (usually 0)"
010h N*siz Message Info (32bit offset from DAT1+8, and optional attributes)
For each message, there is a INF data about it. At Wii Fit, there is just the message offset. At Wii Fit Plus, there is two another fields.
000h 4 Offset to the message (after DAT1+8 section header)
004h siz-4 Attributes/flags (if entrysize is bigger than 4 bytes)
The optional Attribute bits could be used to select different font types or window styles.
000h 4 Chunk ID "DAT1" ;or "1TAD" in Super Mario 64 DS
004h 4 Chunk Size ;or Size+1 in Super Mario 64 DS
008h .. Message strings (usually UTF-16, depending on Encoding in header)
The DSi Camera uses both char 001Ah and 0025h for escape codes.
UTF-16 string characters:
0000 End of String (except inside Escape sequences)
000A Linebreak
001A,nn,command,parameters Escape Sequences (nn=length in bytes)
001A,08,00,0000,00xx Set font size (64h=100%=Normal Size)
001A,08,00,0001,00xx Set text color to xx
001A,08,01,0000,24xx Draw Unicode char U+2460..246E ;"(1)"..("15)"
001A,08,01,0000,xxxx Draw Unicode char U+E068..F12B ;custom?
001A,06,02,0000 Draw Name of current player
001A,0A,02,0010,000x,000w Draw Integer from index x with w digits
001A,08,02,0011,00xx Unknown (with xx=0..8)
001A,08,02,0012,0000 Draw Name of a player
001A,08,02,0013,0000 Unknown
001A,08,02,0014,0000 Unknown
001A,08,02,0015,0000 Unknown
001A,0A,02,0016,0000,0000 Unknown
001A,08,02,0017,0000 Unknown
001A,08,02,0020,0000 Draw Name of a Wii friend
001A,08,03,0010,0000 Unknown
001A,0C,04,0000,000x,yyyy,zzzz Unknown (x=0..1, y=0524..14A4, and z=y+1)
0025,00xx,00yy,00zz Escape codes in form of "%xyz" (or similar)
00xx ASCII Characters 20h..7Eh
E0xx Custom button symbols (eg. in DSi Launcher)
Shift-JIS (or whatever) strings in Super Mario 64 DS:
0D Linebreak?
10..1F Escape codes?
xx,xx Unknown (doesn't really look like english Shift-JIS characters)
FF End of String
000h 4 Chunk ID "MID1"
004h 4 Chunk Size
008h 2 Number of messages (same as in INF1 block)
00Ah 2 Unknown (usually 1000h)
00Ch 4 Padding (0)
010h 4*N Message IDs
Messages can be repeated in the different files (such like menu and ingame), if so, all messages with the same message ID of the same language are always having the same text, no differences between the text files can be found.
“Elements with the same table index are attributes (not IDs?) for the same string.”
Some games are reportedly having additional “FLW1” and “FLT1” chunks. Unknown which games, and unknown what for, and unknown if such chunks exist in any DS games (or only in Wii games or whatever).
Thanks to
Manual files are common in DSiware download titles, unknown if DS/DSi cartridges did also use that files (or if they did stick with print manuals).
Used in DSi Flipnote, Sudoku, Paper Plane, Deep Psyche, Dr. Mario, DSi-to-3DS Transfer Tool.
Usually found in “rom:..\manpages_narc.blz” (with two folders in the .blz file, “arc” for the manual, and “gpArc” with a quick reference on how to read the manual; the latter containing only two “pages”, without “content” file).
000h 4 ID "NTLI"
004h 2 Byte Order (FEFFh)
006h 2 Version (can be 0200h)
008h 4 Total Filesize
00Ch 2 Header Size (10h)
00Eh 4 Number of Chunks (usually 1 = mtl1)
000h 4 Chunk ID "mtl1"
004h 4 Chunk Size
008h 4 Number of supported languages
00Ch 2*N Language IDs (two-letter ASCII spelled backwards)
Known IDs are ne=English, rf=French, se=Spanish, ti=Italian, ed=German.
EUR games do usually support 5 languages (Deep Psyche supports less).
IDs for Japanese/Chinese/Korean are unknown (also unknown if US has different IDs for english/spanish etc, and if portugese or so do exist on DSi).
Note: The two-letters do directly translate to the “ntmc\xx" and “ntpg\xx" folder names.
The content contains “hyperlinks” to the actual chapters. A chapter can be divided into subsections (with the subsections being shown at the right of the main chapter name).
000h 4 ID "NTMC"
004h 2 Byte Order (FEFFh)
006h 2 Version (can be 0200h)
008h 4 Total Filesize
00Ch 2 Header Size (10h)
00Eh 4 Number of Chunks (usually 3 = nap1+txp1+mtc1)
000h 4 Chunk ID "nap1"
004h 4 Chunk Size
008h 4 Number of chapters (aka pages) minus 1? (eg. 18h=19h)
00Ch 4*N Offsets to filenames (from nap+0Ch)
... .. Filenames (ASCII, terminated by 00h)
000h 4 Chunk ID "txp1"
004h 4 Chunk Size
008h 4 Number of something?? minus 1 (eg. 25h=26h)
00Ch 4*N Offsets to something?? (from txp1+0Ch)
... .. Somewhat corrupt UTF-16 strings (many aborted with char 20xxh)
000h 4 Chunk ID "mtc1"
004h 4 Chunk Size
008h 4 Number of dunno what (eg. 0Dh=Much more?)
00Ch 2*? 16bit Indices in txp1? (eg. 0000h..0025h)
The pages can contain text, tables, and symbols/images/screenshots (there appears to be no support for hyperlinks inside of the pages; probably because they default to be displayed on upper screen, without touchscreen support).
000h 4 ID "NTPC"
004h 2 Byte Order (FEFFh)
006h 2 Version (can be 0200h)
008h 4 Total Filesize
00Ch 2 Header Size (10h)
00Eh 4 Number of Chunks (usually 7 = nap1+txp1+pag1+pan1+pas1+txt1+pae1)
000h 4 Chunk ID "nap1"
004h 4 Chunk Size (10h)
008h 4 Zero (unlike as in NTMC file)
00Ch 4 Unknown (4)
000h 4 Chunk ID "txp1"
004h 4 Chunk Size
008h 4 Number of something?? minus 1 (eg. 02h=03h)
00Ch 4*N Offsets to something?? (from txp1+0Ch)
... .. UTF-16 strings (Headline, Body, Footer?)
000h 4 Chunk ID "pag1"
004h 4 Chunk Size (10h)
008h 2? 0000h text color black?
00Ah 2? 0160h link color or so?
00Ch 2? 7FFFh bg color white?
00Eh 2? 0000h
000h 4 Chunk ID "pan1"
004h 4 Chunk Size (10h)
008h 2? 0000h
00Ah 2? 0000h
00Ch 2? 0100h
00Eh 2? 0160h link color or so?
000h 4 Chunk ID "pan1"
004h 4 Chunk Size (0Ch)
008h 4? 00000001h
000h 4 Chunk ID "txt1"
004h 4 Chunk Size
008h 2? 0008h
00Ah 2? 0008h
00Ch 2? 00F0h
00Eh 2? 0150h
010h 2? 0001h
012h 2? 0015h Number of 8-byte entries? start/end line-wrapping list?
014h 2? 000Dh
016h 2? 0010h
018h 4? 00000000h
01Ch 4? 00001CE7h
020h 4? 00000000h
024h 4? 00000000h
028h N*8 Unknown 8-byte entries? (00xxh,0010h,0000h,00yyh)
Or maybe positioning for symbols/images/tables?
000h 4 Chunk ID "pae1"
004h 4 Chunk Size (08h)
000h 2 Bitmap Width in bytes (eg. 0Fh, 10h, 14h, 40h, 60h)
002h 2 Bitmap Height (eg. 12h, 0Eh, 14h, 30h, 47h)
004h 1 Unknown (04h) (maybe color depth)
005h 1 Unknown (00h or 01h) (often same as [007h], but not always)
006h 2 Number of Palette entries (usually 10h, 80h, or 100h)
008h 8 Zerofilled
010h .. Palette data (with 16bit values in range 0000h..7FFFh)
... .. Bitmap data (seems to be always 8bpp)
DSi manuals are using the TWLFontTable.dat system font (Flipnote, at least).
3DS manuals consist of CLYT and CLIM files (inside of a manual BCMA darc archive in a NCCH file).
2.4GHz band, Wireless LAN (WLAN) IEEE802.11b protocol
A very large part of the DS Wifi chapters is based on Stephen Stair’s great DS Wifi document, thanks there.
Wifi Registers & RAM cannot be written to by STRB opcodes (ignored).
Address Dir Name r/w [Init] Description
4808000h R W_ID ---- [1440] Chip ID (1440h=DS, C340h=DS-Lite)
4808004h R/W W_MODE_RST 9fff [0000] Mode/Reset
4808006h R/W W_MODE_WEP --7f [0000] Mode/Wep modes
4808008h R/W W_TXSTATCNT ffff [0000] Beacon Status Request
480800Ah R/W W_X_00Ah ffff [0000] [bit7 - ingore rx duplicates]
4808010h R/W W_IF ackk [0000] Wifi Interrupt Request Flags
4808012h R/W W_IE ffff [0000] Wifi Interrupt Enable
4808018h R/W W_MACADDR_0 ffff [0000] Hardware MAC Address, 1st 2 bytes
480801Ah R/W W_MACADDR_1 ffff [0000] Hardware MAC Address, next 2 bytes
480801Ch R/W W_MACADDR_2 ffff [0000] Hardware MAC Address, last 2 bytes
4808020h R/W W_BSSID_0 ffff [0000] BSSID (first 2 bytes)
4808022h R/W W_BSSID_1 ffff [0000] BSSID (next 2 bytes)
4808024h R/W W_BSSID_2 ffff [0000] BSSID (last 2 bytes)
4808028h R/W W_AID_LOW ---f [0000] usually as lower 4bit of AID value
480802Ah R/W W_AID_FULL -7ff [0000] AID value assigned by a BSS.
480802Ch R/W W_TX_RETRYLIMIT ffff [0707] Tx Retry Limit (set from 00h-FFh)
480802Eh R/W W_INTERNAL ---1 [0000]
4808030h R/W W_RXCNT ff0e [0000] Receive control
4808032h R/W W_WEP_CNT ffff [0000] WEP engine enable
4808034h R? W_INTERNAL 0000 [0000] bit0,1 (see ports 004h,040h,1A0h)
4808036h R/W W_POWER_US ---3 [0001]
4808038h R/W W_POWER_TX ---7 [0003]
480803Ch R/W W_POWERSTATE -r-2 [0200]
4808040h R/W W_POWERFORCE 8--1 [0000]
4808044h R W_RANDOM 0xxx [0xxx]
4808048h R/W W_POWER_? ---3 [0000]
4808050h R/W W_RXBUF_BEGIN ffff [4000]
4808052h R/W W_RXBUF_END ffff [4800]
4808054h R W_RXBUF_WRCSR 0rrr [0000]
4808056h R/W W_RXBUF_WR_ADDR -fff [0000]
4808058h R/W W_RXBUF_RD_ADDR 1ffe [0000]
480805Ah R/W W_RXBUF_READCSR -fff [0000]
480805Ch R/W W_RXBUF_COUNT -fff [0000]
4808060h R W_RXBUF_RD_DATA rrrr [xxxx]
4808062h R/W W_RXBUF_GAP 1ffe [0000]
4808064h R/W W_RXBUF_GAPDISP -fff [0000]
4808068h R/W W_TXBUF_WR_ADDR 1ffe [0000]
480806Ch R/W W_TXBUF_COUNT -fff [0000]
4808070h W W_TXBUF_WR_DATA xxxx [xxxx]
4808074h R/W W_TXBUF_GAP 1ffe [0000]
4808076h R/W W_TXBUF_GAPDISP 0fff [0000]
4808078h W W_INTERNAL mirr [mirr] Read: Mirror of 068h
4808080h R/W W_TXBUF_BEACON ffff [0000] Beacon Transmit Location
4808084h R/W W_TXBUF_TIM --ff [0000] Beacon TIM Index in Frame Body
4808088h R/W W_LISTENCOUNT --ff [0000] Listen Count
480808Ch R/W W_BEACONINT -3ff [0064] Beacon Interval
480808Eh R/W W_LISTENINT --ff [0000] Listen Interval
4808090h R/W W_TXBUF_CMD ffff [0000] Multiplay Command
4808094h R/W W_TXBUF_REPLY1 ffff [0000] Multiplay Next Reply
4808098h R W_TXBUF_REPLY2 0000 [0000] Multiplay Current Reply
480809Ch R/W W_INTERNAL ffff [0050] value 4x00h --> preamble+x*12h us?
48080A0h R/W W_TXBUF_LOC1 ffff [0000]
48080A4h R/W W_TXBUF_LOC2 ffff [0000]
48080A8h R/W W_TXBUF_LOC3 ffff [0000]
48080ACh W W_TXREQ_RESET fixx [0050]
48080AEh W W_TXREQ_SET fixx [0050]
48080B0h R W_TXREQ_READ --1f [0010]
48080B4h W W_TXBUF_RESET 0000 [0000] (used by firmware part4)
48080B6h R W_TXBUSY 0000 [0000] (used by firmware part4)
48080B8h R W_TXSTAT 0000 [0000]
48080BAh ? W_INTERNAL 0000 [0000]
48080BCh R/W W_PREAMBLE ---3 [0001]
48080C0h R/W x W_CMD_TOTALTIME ffff [0000] (used by firmware part4)
48080C4h R/W x W_CMD_REPLYTIME ffff [0000] (used by firmware part4)
48080C8h ? W_INTERNAL 0000 [0000]
48080D0h R/W W_RXFILTER 1fff [0401]
48080D4h R/W W_CONFIG_0D4h ---3 [0001]
48080D8h R/W W_CONFIG_0D8h -fff [0004]
48080DAh R/W W_RX_LEN_CROP ffff [0602]
48080E0h R/W W_RXFILTER2 ---f [0008]
48080E8h R/W W_US_COUNTCNT ---1 [0000] Microsecond counter enable
48080EAh R/W W_US_COMPARECNT ---1 [0000] Microsecond compare enable
48080ECh R/W W_CONFIG_0ECh 3f1f [3F03]
48080EEh R/W W_CMD_COUNTCNT ---1 [0001]
48080F0h R/W W_US_COMPARE0 fc-- [FC00] Microsecond compare, bits 0-15
48080F2h R/W W_US_COMPARE1 ffff [FFFF] Microsecond compare, bits 16-31
48080F4h R/W W_US_COMPARE2 ffff [FFFF] Microsecond compare, bits 32-47
48080F6h R/W W_US_COMPARE3 ffff [FFFF] Microsecond compare, bits 48-63
48080F8h R/W W_US_COUNT0 ffff [0000] Microsecond counter, bits 0-15
48080FAh R/W W_US_COUNT1 ffff [0000] Microsecond counter, bits 16-31
48080FCh R/W W_US_COUNT2 ffff [0000] Microsecond counter, bits 32-47
48080FEh R/W W_US_COUNT3 ffff [0000] Microsecond counter, bits 48-63
4808100h ? W_INTERNAL 0000 [0000]
4808102h ? W_INTERNAL 0000 [0000]
4808104h ? W_INTERNAL 0000 [0000]
4808106h ? W_INTERNAL 0000 [0000]
480810Ch R/W W_CONTENTFREE ffff [0000] ...
4808110h R/W W_PRE_BEACON ffff [0000]
4808118h R/W W_CMD_COUNT ffff [0000]
480811Ch R/W W_BEACON_COUNT ffff [0000] reloaded with W_BEACONINT
4808120h R/W W_CONFIG_120h 81ff [0048] init from firmware[04Ch]
4808122h R/W W_CONFIG_122h ffff [4840] init from firmware[04Eh]
4808124h R/W W_CONFIG_124h ffff [0000] init from firmware[05Eh], or 00C8h
4808126h ? W_INTERNAL fixx [ 0080]
4808128h R/W W_CONFIG_128h ffff [0000] init from firmware[060h], or 07D0h
480812Ah ? W_INTERNAL fixx [1000] lower 12bit same as W_CONFIG_128h
4808130h R/W W_CONFIG_130h -fff [0142] init from firmware[054h]
4808132h R/W W_CONFIG_132h 8fff [8064] init from firmware[056h]
4808134h R/W W_POST_BEACON ffff [FFFF] ...
4808140h R/W W_CONFIG_140h ffff [0000] init from firmware[058h], or xx
4808142h R/W W_CONFIG_142h ffff [2443] init from firmware[05Ah]
4808144h R/W W_CONFIG_144h --ff [0042] init from firmware[052h]
4808146h R/W W_CONFIG_146h --ff [0016] init from firmware[044h]
4808148h R/W W_CONFIG_148h --ff [0016] init from firmware[046h]
480814Ah R/W W_CONFIG_14Ah --ff [0016] init from firmware[048h]
480814Ch R/W W_CONFIG_14Ch ffff [162C] init from firmware[04Ah]
4808150h R/W W_CONFIG_150h ff3f [0204] init from firmware[062h], or 202h
4808154h R/W W_CONFIG_154h 7a7f [0058] init from firmware[050h]
4808158h W W_BB_CNT mirr [00B5] BB Access Start/Direction/Index
480815Ah W W_BB_WRITE ???? [0000] BB Access data byte to write
480815Ch R W_BB_READ 00rr [00B5] BB Access data byte read
480815Eh R W_BB_BUSY 000r [0000] BB Access Busy flag
4808160h R/W W_BB_MODE 41-- [0100] BB Access Mode
4808168h R/W W_BB_POWER 8--f [800D] BB Access Powerdown
480816Ah ? W_INTERNAL 0000 [0001] (or 0000h?)
4808170h ? W_INTERNAL 0000 [0000]
4808172h ? W_INTERNAL 0000 [0000]
4808174h ? W_INTERNAL 0000 [0000]
4808176h ? W_INTERNAL 0000 [0000]
4808178h W W_INTERNAL fixx [0800] Read: mirror of 17Ch
480817Ch R/W W_RF_DATA2 ffff [0800]
480817Eh R/W W_RF_DATA1 ffff [C008]
4808180h R W_RF_BUSY 000r [0000]
4808184h R/W W_RF_CNT 413f [0018]
4808190h R/W W_INTERNAL ffff [0000]
4808194h R/W W_TX_HDR_CNT ---7 [0000] used by firmware part4 (0 or 6)
4808198h R/W W_INTERNAL ---f [0000]
480819Ch R W_RF_PINS fixx [0004]
48081A0h R/W W_X_1A0h -933 [0000] used by firmware part4 (0 or 823h)
48081A2h R/W W_X_1A2h ---3 [0001] used by firmware part4
48081A4h R/W W_X_1A4h ffff [0000] "Rate used when signal test..."
48081A8h R W_RXSTAT_INC_IF rrrr [0000] Stats Increment Flags
48081AAh R/W W_RXSTAT_INC_IE ffff [0000] Stats Increment IRQ Enable
48081ACh R W_RXSTAT_OVF_IF rrrr [0000] Stats Half-Overflow Flags
48081AEh R/W W_RXSTAT_OVF_IE ffff [0000] Stats Half-Overflow IRQ Enable
48081B0h R/W W_RXSTAT --ff [0000]
48081B2h R/W W_RXSTAT ffff [0000] RX_LengthRateErrorCount
48081B4h R/W W_RXSTAT rrff [0000] ... firmware uses also MSB ... ?
48081B6h R/W W_RXSTAT ffff [0000]
48081B8h R/W W_RXSTAT --ff [0000]
48081BAh R/W W_RXSTAT --ff [0000]
48081BCh R/W W_RXSTAT ffff [0000]
48081BEh R/W W_RXSTAT ffff [0000]
48081C0h R/W W_TX_ERR_COUNT --ff [0000] TransmitErrorCount
48081C4h R W_RX_COUNT fixx [0000]
[1D0 - 1DE are 15 entries related to multiplayer response errors]
48081D0h R/W W_CMD_STAT ff-- [0000]
48081D2h R/W W_CMD_STAT ffff [0000]
48081D4h R/W W_CMD_STAT ffff [0000]
48081D6h R/W W_CMD_STAT ffff [0000]
48081D8h R/W W_CMD_STAT ffff [0000]
48081DAh R/W W_CMD_STAT ffff [0000]
48081DCh R/W W_CMD_STAT ffff [0000]
48081DEh R/W W_CMD_STAT ffff [0000]
48081F0h R/W W_INTERNAL ---3 [0000]
4808204h ? W_INTERNAL fixx [0000]
4808208h ? W_INTERNAL fixx [0000]
480820Ch W W_INTERNAL fixx [0050]
4808210h R W_TX_SEQNO fixx [0000]
4808214h R W_RF_STATUS XXXX [0009] (used by firmware part4)
480821Ch W W_IF_SET fbff [0000] Force Interrupt (set bits in W_IF)
4808220h R/W W_RAM_DISABLE ffff [0000] WifiRAM control
4808224h R/W W_INTERNAL ---3 [0003]
4808228h W W_X_228h fixx [0000] (used by firmware part4) (bit3)
4808230h R/W W_INTERNAL --ff [0047]
4808234h R/W W_INTERNAL -eff [0EFF]
4808238h R/W W_INTERNAL ffff [0000] ;rx_seq_no-60h+/-x ;why that?
;other day: fixed value, not seq_no related?
480823Ch ? W_INTERNAL fixx [0000] like W_TXSTAT... ONLY for beacons?
4808244h R/W W_X_244h ffff [0000] (used by firmware part4)
4808248h R/W W_INTERNAL ffff [0000]
480824Ch R W_INTERNAL fixx [0000] ;rx_mac_addr_0 ;\OverTheHedge
480824Eh R W_INTERNAL fixx [0000] ;rx_mac_addr_1 ;/writes FFFFh?
4808250h R W_INTERNAL fixx [0000] ;rx_mac_addr_2
4808254h ? W_CONFIG_254h fixx [0000] (read: FFFFh=DS, EEEEh=DS-Lite)
4808258h ? W_INTERNAL fixx [0000]
480825Ch ? W_INTERNAL fixx [0000]
4808260h ? W_INTERNAL fixx [ 0FEF]
4808264h R W_INTERNAL fixx [0000] ;rx_addr_1 (usually "rxtx_addr-x")
4808268h R W_RXTX_ADDR fixx [0005] ;rxtx_addr
4808270h R W_INTERNAL fixx [0000] ;rx_addr_2 (usually "rx_addr_1-1")
4808274h ? W_INTERNAL fixx [ 0001]
4808278h R/W W_INTERNAL ffff [000F]
480827Ch ? W_INTERNAL fixx [ 000A]
4808290h (R/W) W_X_290h fixx [FFFF] bit 0 = ? (used by firmware part4)
4808298h W W_INTERNAL fixx [0000]
48082A0h R/W W_INTERNAL ffff [0000]
48082A2h R W_INTERNAL XXXX [7FFF] 15bit shift reg (used during tx?)
48082A4h R W_INTERNAL fixx [0000] ;rx_rate_1 not ALWAYS same as 2C4h
48082A8h W W_INTERNAL fixx [0000]
48082ACh ? W_INTERNAL fixx [ 0038]
48082B0h W W_INTERNAL fixx [0000]
48082B4h R/W W_INTERNAL -1-3 [0000]
48082B8h ? W_INTERNAL fixx [0000] ;dsi launcher checks if zero
48082C0h R/W W_INTERNAL ---1 [0000]
48082C4h R W_INTERNAL fixx [000A] ;rx_rate_2 (0Ah,14h = 1,2 Mbit/s)
48082C8h R W_INTERNAL fixx [0000] ;rx_duration/length/rate (or so?)
48082CCh R W_INTERNAL fixx [0000] ;rx_framecontrol; from ieee header
48082D0h DIS W_INTERNAL ;"W_POWERACK" (internal garbage)
;normally DISABLED (unless FORCE)
48082F0h R/W W_INTERNAL ffff [0000]
48082F2h R/W W_INTERNAL ffff [0000]
48082F4h R/W W_INTERNAL ffff [0000]
48082F6h R/W W_INTERNAL ffff [0000]
All other ports in range 4808000h..4808FFFh are unused.
All registers marked as “W_INTERNAL” aren’t used by Firmware part4, and are probably unimportant, except for whatever special diagnostics purposes.
Reading from write-only ports (W) often mirrors to data from other ports.
Additionally, there are 69h Baseband Chip Registers (BB), and 0Fh RF Chip Registers (see BB and RF chapters).
For Wifi Power Managment (POWCNT2), for Wifi Waitstates (WIFIWAITCNT), see:
DS Power Control For the Power LED Blink Feature (conventionally used to indicate Wifi activity) see:
For Wifi Configuration and Calibration data in Firmware Header, see:
4804000h W_MACMEM RX/TX Buffers (2000h bytes) (excluding below specials)
4805F60h Used for something, not included in the rx circular buffer.
4805F80h W_WEPKEY_0 (32 bytes)
4805FA0h W_WEPKEY_1 (32 bytes)
4805FC0h W_WEPKEY_2 (32 bytes)
4805FE0h W_WEPKEY_3 (32 bytes)
Unlike all other NDS memory, Wifi RAM is left uninitialized after boot.
Nintendo’s software does init/change several values:
[480xxxxh]=5A5Ah/A5A5h ;-initial dummy WifiRAM memfill values
[4805F70h]=FFFFh ;\
[4805F72h]=FFFFh ; set to FFFFh by software
[4805F76h]=FFFFh ;
[4805F7Eh]=FFFFh ;/
The hardware does update several values (unless disabled in W_RAM_DISABLE.bit5):
[4805F6Eh]=0F00h (nothing received), or 0F01h (received something)
And, if received packet was accepted (eg. when W_RXFILTER.bit0=1=AcceptAll):
[4805F70h]=Received MAC Address (6 bytes, looks same as port 480824Ch)
[4805F76h]=xxx0h (increasing value, Sequence Control from packet header)
And, in multiplay slave mode (when receiving CMD packets?):
[4805F7Eh]=xxx0h (next higher sequence number? ie. [4805F76h]+10h)
These WEP key slots store the WEP keys that are used for encryption for 802.11 keys IDs 0-3.
0-15 Chip ID (1440h on NDS, C340h on NDS-lite)
The NDS-lite is more or less backwards compatible with the original NDS (the W_RXBUF_GAPDISP and W_TXBUF_GAPDISP are different, and most of the garbage effects on unused/mirrored ports are different, too).
0 Adjust some ports (0/1=see lists below) (R/W)
TX Master Enable for LOC1..3 and Beacon (0=Disable, 1=Enable)
1-12 Unknown (R/W)
13 Reset some ports (0=No change, 1=Reset/see list below) (Write-Only)
14 Reset some ports (0=No change, 1=Reset/see list below) (Write-Only)
15 Unknown (R/W)
0-2 Unknown, specify a software mode for wifi operation
(may be related to hardware but a correlation has not yet been found)
3-5 WEP Encryption Key Size:
0=Reserved (acts same as 1)
1=64bit WEP (IV=24bit + KEY=40bit) (aka 3+5 bytes) ;standard/us
2=128bit WEP (IV=24bit + KEY=104bit) (aka 3+13 bytes) ;standard/world
3=152bit WEP (IV=24bit + KEY=128bit) (aka 3+16 bytes) ;uncommon
4=Unknown, mabye 256bit WEP (IV=24bit + KEY=232bit) (aka 3+29 bytes)?
5=Reserved (acts same as 1)
6=Reserved (acts same as 1)
7=Reserved (acts same as 1)
6 Unknown
7-15 Always zero
48bit MAC Address of the console. Should be initialized from firmware[036h]. The hardware receives only packets that are sent to this address (or to group addresses, like FF:FF:FF:FF:FF:FF).
48bit BSSID stored here. Ie. the MAC address of the host, obtained from Beacon frames (on the host itself, that should be just same as W_MACADDR). See W_RXFILTER.
Bit0-3 Multiplay Slave number (1..15, or 0)
Bit4-15 Not used
Usually set to zero, or equal to the lower 4bit of the W_AID_FULL value.
Bit0-10 Association ID (AID) (1..2007, or zero)
Bit11-15 Not used
0-14 Unknown (usually zero)
15 WEP Engine Enable (0=Disable, 1=Enable)
Normally, bit15 should be always set (but it will only affect WEP-encrypted packets, ie. packets with Frame Control bit14=1 in 802.11 header). When disabled, WEP packets aren’t received at all (neither in encrypted nor decrypted form), and sending WEP packets might be also ignored(?).
The firmware contains some code that does toggle the bit off for a moment (apparently to reset something after transfer errors).
0-10 Random
11-15 Not used (zero)
The random generator is updated at 33.51MHz rate, as such:
X = (X AND 1) XOR (X ROL 1) ;(rotation within 11bit range)
That random sequence goes through 5FDh different values before it restarts.
When reading from the random register, the old latched value is returned to the CPU, and the new current random value is then latched, so reads always return the older value, timed from the previous read.
Occassionally, about once every some thousand reads, the latching appears to occur 4 cycles earlier than normally, so the value on the next read will be 4 cycles older than expected.
The random register has ACTIVE mirrors.
Bit Dir Expl.
0 R/W Unknown (this does NOT affect TX)
1 R/W Preamble (0=Long, 1=Short) (this does NOT affect TX)
2 W Preamble (0=Long, 1=Short) (this does affect TX) (only at 2Mbit/s)
3-15 - Always zero
Short preamble works only with 2Mbit/s transfer rate (ie. when set as so in TX hardware header). 1Mbit/s rate always uses long preamble.
Type Carrier Signal SFD Value PLCP Header Data
Long 128bit, 1Mbit 16bit, 1Mbit 48bit, 1Mbit N bits, 1Mbit or 2Mbit
Short 56bit, 1Mbit 16bit, 1Mbit 48bit, 2Mbit N bits, 2Mbit
Length of the Carrier+SFD+PLCP part is thus 192us (long) or 96us (short).
Note: The Carrier+SFD+PLCP part is sent between IRQ14 and IRQ07 (not between IRQ07 and IRQ01).
Firmware also changes W_CONFIG_140h alongsides with the preamble setting.
[4808034h]=0002h ;W_INTERNAL
[480819Ch]=0046h ;W_RF_PINS
[4808214h]=0009h ;W_RF_STATUS
[480827Ch]=0005h ;W_INTERNAL
[48082A2h]=? ;...unstable?
[480827Ch]=000Ah ;W_INTERNAL
[4808056h]=0000h ;W_RXBUF_WR_ADDR
[48080C0h]=0000h ;W_CMD_TOTALTIME
[48080C4h]=0000h ;W_CMD_REPLYTIME
[48081A4h]=0000h ;W_X_1A4h
[4808278h]=000Fh ;W_INTERNAL
...Also, following may be affected (results are unstable though)...
[48080AEh]=? ;or rather the actual port (which it is an mirror of)
[48080BAh]=? ;W_INTERNAL (occassionally unstable)
[4808204h]=? ;W_INTERNAL
[480825Ch]=? ;W_INTERNAL
[4808268h]=? ;W_RXTX_ADDR
[4808274h]=? ;W_INTERNAL
[4808006h]=0000h ;W_MODE_WEP
[4808008h]=0000h ;W_TXSTATCNT
[480800Ah]=0000h ;W_X_00Ah
[4808018h]=0000h ;W_MACADDR_0
[480801Ah]=0000h ;W_MACADDR_1
[480801Ch]=0000h ;W_MACADDR_2
[4808020h]=0000h ;W_BSSID_0
[4808022h]=0000h ;W_BSSID_1
[4808024h]=0000h ;W_BSSID_2
[4808028h]=0000h ;W_AID_LOW
[480802Ah]=0000h ;W_AID_FULL
[480802Ch]=0707h ;W_TX_RETRYLIMIT
[480802Eh]=0000h ;W_INTERNAL
[4808050h]=4000h ;W_RXBUF_BEGIN
[4808052h]=4800h ;W_RXBUF_END
[4808084h]=0000h ;W_TXBUF_TIM
[48080BCh]=0001h ;W_PREAMBLE
[48080D0h]=0401h ;W_RXFILTER
[48080D4h]=0001h ;W_CONFIG_0D4h
[48080E0h]=0008h ;W_RXFILTER2
[48080ECh]=3F03h ;W_CONFIG_0ECh
[4808194h]=0000h ;W_TX_HDR_CNT
[4808198h]=0000h ;W_INTERNAL
[48081A2h]=0001h ;W_X_1A2h
[4808224h]=0003h ;W_INTERNAL
[4808230h]=0047h ;W_INTERNAL
0 Receive Complete (packet received and stored in the RX fifo)
1 Transmit Complete (packet is done being transmitted) (no matter if error)
2 Receive Event Increment (IRQ02, see W_RXSTAT_INC_IE)
3 Transmit Error Increment (IRQ03, see W_TX_ERR_COUNT)
4 Receive Event Half-Overflow (IRQ04, see W_RXSTAT_OVF_IE)
5 Transmit Error Half-Overflow (IRQ05, see W_TX_ERR_COUNT.Bit7)
6 Start Receive (IRQ06, a packet has just started to be received)
7 Start Transmit (IRQ07, a packet has just started to be transmitted)
8 Txbuf Count Expired (IRQ08, see W_TXBUF_COUNT)
9 Rxbuf Count Expired (IRQ09, see W_RXBUF_COUNT)
10 Not used (always zero, even when trying to set it with W_IF_SET)
11 RF Wakeup (IRQ11, see W_POWERSTATE)
12 Multiplay CMD done (or failed) (IRQ12, see W_CMD_COUNT)
13 Post-Beacon Timeslot (IRQ13, see W_POST_BEACON)
14 Beacon Timeslot (IRQ14, see W_BEACON_COUNT/W_US_COMPARE)
15 Pre-Beacon Timeslot (IRQ15, see W_BEACON_COUNT/W_PRE_BEACON)
Write a ‘1’ to a bit to clear it. For the Half-Overflow flags that works ONLY if the counter MSBs are zero (ie. one must first read the counters (to reset them), and THEN acknowledge the corresponding W_IF bit).
The Transmit Start/Complete bits (Bit7,1) are set for EACH packet (including beacons, and including retries).
0-15 Enable Flags, same bits as W_IF (0=Disable, 1=Enable)
In W_IE, Bit10 is R/W, but seems to have no function since IRQ10 doesn’t exist.
0-15 Set corresponding bits in W_IF (0=No change, 1=Set Bit)
Notes: Bit10 cannot be set since no IRQ10 exists. This register does only set IRQ flags, but without performing special actions (such like W_BEACON_COUNT and W_POST_BEACON reloads that occur on real IRQ14’s).
IF.Bit24 gets set <only> when (W_IF AND W_IE) changes from 0000h to non-zero.
IF.Bit24 can be reset (ack) <even> when (W_IF AND W_IE) is still non-zero.
Caution Caution Caution Caution Caution
That means, when acknowledging IF.Bit24, then NO FURTHER wifi IRQs
will be executed whilst and as long as (W_IF AND W_IE) is non-zero.
One work-around is to process/acknowledge ALL wifi IRQs in a loop, including further IRQs that may occur inside of that loop, until (W_IF AND W_IE) becomes 0000h.
Another work-around (for single IRQs) would be to acknowledge IF and W_IF, and then to set W_IE temporarily to 0000h, and then back to the old W_IE setting.
0 Disable W_US_COUNT and W_BB_ports (0=Enable, 1=Disable)
1 Unknown (usually 0)
2-15 Always zero
Bit0=0 enables RFU by setting RFU.Pin11=HIGH, which activates the 22.000MHz oscillator on the RFU board, the 22MHz clock is then output to RFU.Pin26.
transmit-related power save or sth
init from firmware[05Ch]
0 Auto Wakeup (1=Leave Idle Mode a while after Pre-Beacon IRQ15)
1 Auto Sleep (0=Enter Idle Mode on Post-Beacon IRQ13)
2 Unknown
3 Unknown (Write-only) (used by firmware)
4-15 Always zero
0 Unknown (usually 0) (R/W)
1 Request Power Enable (0=No, 1=Yes/queued) (R/W, but not always)
2-7 Always zero
8 Indicates that Bit9 is about the be cleared (Read only)
9 Current power state (0=Enabled, 1=Disabled) (Read only)
10-15 Always zero
[value =1: queue disable power state] ;<– seems to be incorrect
[value =2: queue enable power state] ;<– seems to be correct
Enabling causes wakeup interrupt (IRQ11).
Note: That queue stuff seems to work only if W_POWER_US=0 and W_MODE_RST=1.
0 New value for W_POWERSTATE.Bit9 (0=Clear/Delayed, 1=Set/Immediately)
1-14 Always zero
15 Apply Bit0 to W_POWERSTATE.Bit9 (0=No, 1=Yes)
Setting W_POWERFORCE=8001h whilst W_POWERSTATE.Bit9=0 acts immediately:
(Doing this is okay. Switches to power down mode. Similar to IRQ13.)
[4808034h]=0002h ;W_INTERNAL
[480803Ch]=02xxh ;W_POWERSTATE
[48080B0h]=0000h ;W_TXREQ_READ
[480819Ch]=0046h ;W_RF_PINS
[4808214h]=0009h ;W_RF_STATUS (idle)
Setting W_POWERFORCE=8000h whilst W_POWERSTATE.Bit9=1 acts delayed:
(Don't do this. After that sequence, the hardware seems to be messed up)
W_POWERSTATE.Bit8 gets set to indicate the pending operation,
while pending, changes to W_POWERFORCE aren't applied to W_POWERSTATE,
while pending, W_POWERACK becomes Read/Write-able,
writing 0000h to W_POWERACK does clear W_POWERSTATE.Bit8,
and does apply POWERFORCE.Bit0 to W_POWERSTATE.Bit9
and does deactivate Port W_POWERACK again.
0 Unknown
1 Unknown
2-15 Always zero
At whatever time (during transmit or so) it gets set to 0003h by hardware.
See also: POWCNT2, W_BB_POWER.
0 Copy W_RXBUF_WR_ADDR to W_RXBUF_WRCSR (aka force RX buf empty) (W)
1-3 Unknown (R/W)
4-6 Always zero
7 Copy W_TXBUF_REPLY1 to W_TXBUF_REPLY2, set W_TXBUF_REPLY1 to 0000h (W)
8-14 Unknown (R/W)
15 Enable Queuing received data to RX FIFO (R/W)
0 For Broadcasts? (0=Insist on W_BSSID, 1=Accept no matter of W_BSSID)
1 Unknown (usually zero)
2 Unknown (usually zero)
3 Unknown (usually zero)
4 Unknown (usually zero)
5 Unknown (usually zero)
6 Unknown (usually zero)
7 Unknown (0 or 1)
8 Empty Packets (0=Ignore, 1=Accept; with RXHDR[0]=801Fh)
9 Unknown (0 or 1)
10 Unknown (0 or 1) (when set, receives beacons, and maybe others)
11 Unknown (usually zero) ;reportedly "allow toDS" ?
12 Update W_RXBUF_WRCSR after IEEE header (instead after full packets?)
(setting bit12 causes a mess, where new "packets" in RX buf could
either contain RXHDR+IEEE header, or Data corresponding to that
headers, which could be useful only if there's a way to distinguish
between headers and data, and knowing the size of the data blocks).
13-15 Not used (always zero)
Specifies what packets to allow. Some random notes…
0000h = Disable receive.
FFFFh = Enable receive.
0400h = Receives managment frames (and possibly others, too)
The exact meaning of the bits is unknown. The most import part is the address filtering based on the DA and BSSID values in IEEE header:
DA=W_MACADDR is always received
DA=Broadcast, and BSSID=W_BSSID is always received
DA=Broadcast, and BSSID=other is received only if RXFILTER.bit0=1
Broadcast addresses (aka group addresses) have DA.firstbyte.bit0=1. RXFILTER.bit0 should be set when searching beacons, and cleared after connecting to an access point.
0 Unknown (0=Receive Data Frames, 1=Ignore Data Frames) (?)
1 Unknown
2 Unknown
3 Unknown (usually set)
4-15 Not used (always zero)
Firmware writes values 08h, 0Bh, 0Dh (aka 1000b, 1011b, 1101b).
Firmware usually has bit0 set, even when receiving data frames, so, in some situations data frames seem to pass-through even when bit0 is set…? Possibly that situation is when W_BSSID matches…?
Control/PS-Poll frames seem to be passed always (even if W_RXFILTER2=0Fh).
The dimensions of the circular Buffer are set with BEGIN/END values, hardware automatically wraps to BEGIN when an incremented pointer hits END address.
Memory between WRCSR and READCSR is free for receiving data, the hardware writes incoming packets to this region (to WRCSR and up) (but without exceeding READCSR), once when it has successfully received a complete packet, the hardware moves WRCSR after the packet (aligned to a 4-byte boundary).
Memory between READCSR and WRCSR contains received data, which can be read by the CPU via RD_ADDR and RD_DATA registers (or directly from memory). Once when having processed that data, the CPU must set READCSR to the end of it.
0-15 Byte-offset in Wifi Memory (usually 4000h..5FFEh)
Although the full 16bit are R/W, only the 12bit halfword offset in Bit1-12 is actually used, the other bits seem to have no effect.
Some or all (?) of the below incrementing registers are automatically matched to begin/end, that is, after incrementing, IF adr=end THEN adr=begin.
0-11 Halfword Address in RAM
12-15 Always zero
Hardware does set this register pointing to end of the most recently received packet (plus 32bit alignment padding). Hardware will write the next packet to that address. And more importantly, software can read from RX fifo up to that address.
0-11 Halfword Address in RAM
12-15 Always zero
This is a value that is latched into W_RXBUF_WRCSR, when the W_RXCNT latch bit (W_RXCNT.Bit0) is written.
0 Always zero
1-12 Halfword Address in RAM for reading via W_RXBUF_RD_DATA
13-15 Always zero
The circular buffer limits are the same as the range specified for the receive FIFO, however the address can be set outside of that range and will only be affected by the FIFO boundary if it crosses the FIFO end location by reading from the circular buffer.
0-11 Halfword Address in RAM
12-15 Always zero
This value is specified the same as W_RXBUF_WRCSR - it’s purely software controlled so it’s up to the programmer to move the start cursor after loading a packet. If W_RXBUF_READCSR != W_RXBUF_WRCSR, then one or more packets exist in the FIFO that need to be processed (see the section on HW RX Headers, for information on calculating packet lengths). Once a packet has been processed, the software should advance the read cursor to the beginning of the next packet.
0-15 Data
returns the 16bit value at the address specified by W_RXBUF_RD_ADDR, and increments W_RXBUF_RD_ADDR by 2. If the increment causes W_RXBUF_RD_ADDR to equal the address specified in W_RXBUF_END, W_RXBUF_RD_ADDR will be reset to the address specified in W_RXBUF_BEGIN.
Ports 1060h, 6060h, 7060h are PASSIVE mirrors of 0060h, reading from these mirrors returns the old latched value from previous read from 0060h, but without reading a new value from RAM, and without incrementing the address.
0 Always zero
1-12 Halfword Address in RAM
13-15 Always zero
Seems to be intended to define a “gap” in the circular buffer, done like so:
Addr=Addr+2 and 1FFEh ;address increment (by W_RXBUF_RD_DATA read)
if Addr=RXBUF_END then ;normal begin/end wrapping (done before gap wraps)
Addr=RXBUF_BEGIN
if Addr=RXBUF_GAP then ;now gap-wrap (may include further begin/end wrap)
Addr=RXBUF_GAP+RXBUF_GAPDISP*2
if Addr>=RXBUF_END then Addr=Addr+RXBUF_BEGIN-RXBUF_END ;wrap more
To disable the gap stuff, set both W_RXBUF_GAP and W_RXBUF_GAPDISP to zero.
0-11 Halfword Offset, used with W_RXBUF_GAP (see there)
12-15 Always zero
Caution: On the DS-Lite, after adding it to W_RXBUF_RD_ADDR, the W_RXBUF_GAPDISP setting is destroyed (reset to 0000h) by hardware. The original DS leaves W_RXBUF_GAPDISP intact.
0-11 Decremented on reads from W_RXBUF_RD_DATA
12-15 Always zero
Triggers IRQ09 when it reaches zero, and does then stay at zero (without further decrementing, and without generating further IRQs).
Note: Also decremented on (accidental) writes to read-only W_RXBUF_RD_DATA.
0-12 Increment Flags (see Port 48081B0h..1BFh)
13-15 Always zero
Bitmask for which statistics have been increased at least once.
Unknown how to reset/acknowledge these bits (neither reading/writing 48081A8h, nor reading 48081B0h..1BFh, nor writing 48081ACh seem to work).
0-12 Counter Increment Interrupt Enable (see 48081B0h..1BFh) (1=Enable)
13-15 Unknown (usually zero)
Statistic Interrupt Enable Control register for Count Up.
Note: ——> seems to trigger IRQ02 …?
0-12 Half-Overflow Flags (see Port 48081B0h..1BFh)
13-15 Always zero
The W_RXSTAT_OVF_IF bits are simply containing the current bit7-value of the corresponding counters, setting or clearing that counter bits is directly reflected to W_RXSTAT_OVF_IF.
The recommended way to acknowledge W_RXSTAT_OVF_IF is to read the corresponding counters (which are reset to 00h after reading). For some reason, the firmware is additionally writing FFFFh to W_RXSTAT_OVF_IF (that is possibly a bug, or it does acknowledge something internally?).
0-12 Half-Overflow Interrupt Enable (see Port 48081B0h..1BFh) (1=Enable)
13-15 Unknown (usually zero)
Statistic Interrupt Enable for Overflow, bits same as in W_RXSTAT_INC_IE
Note: ——> seems to trigger IRQ04 …?
W_RXSTAT is a collection of 8bit counters, which are incremented upon certain events. These entries are automatically reset to 0000h after reading. Should be accessed with LDRH opcodes (using LDRB to read only 8bits does work, but the read is internally expanded to 16bit, and so, the whole 16bit value will be reset to 0000h).
Port Dir Bit Expl.
48081B0h R/W 0 W_RXSTAT ?
48081B1h - - Always 0 -
48081B2h R/W 1 W_RXSTAT ? "RX_RateErrorCount"
48081B3h R/W 2 W_RXSTAT Length>2348 error
48081B4h R/W 3 W_RXSTAT RXBUF Full error
48081B5h R 4? W_RXSTAT ? (R) (but seems to exist; used by firmware)
48081B6h R/W 5 W_RXSTAT Length=0 or Wrong FCS Error
48081B7h R/W 6 W_RXSTAT Packet Received Okay
(also increments on W_MACADDR mis-match)
(also increments on internal ACK packets)
(also increments on invalid IEEE type=3)
(also increments TOGETHER with 1BCh and 1BEh)
(not incremented on RXBUF_FULL error)
48081B8h R/W 7 W_RXSTAT ?
48081B9h - - Always 0 -
48081BAh R/W 8 W_RXSTAT ?
48081BBh - - Always 0 -
48081BCh R/W 9 W_RXSTAT WEP Error (when FC.Bit14 is set)
48081BDh R/W 10 W_RXSTAT ?
48081BEh R/W 11 W_RXSTAT (duplicated sequence control)
48081BFh R/W 12 W_RXSTAT ?
0-? Receive Okay Count (increments together with ports 48081B4h, 48081B7h)
8-? Receive Error Count (increments together with ports 48081B3h, 48081B6h)
Increments when receiving a packet. Automatically reset to zero after reading.
The multiplay error counters are only used when sending a multiplay command (via W_TXBUF_CMD) to any connected slaves (which must be indicated by flags located in the second halfword of the multiplay command’s frame body).
48081D0h Not used (always zero)
48081D1h..1DFh Client 1..15 Response Error (increments on missing replies)
If one or more of those slaves fail to respond, then the corresponding error counters get incremented (at the master side). Automatically reset to zero after reading.
Unknown if these counters do also increment at the slave side?
0-3 Reset corresponding bits in W_TXREQ_READ (0=No change, 1=Reset)
4-15 Unknown (if any)
Firmware writes values 01h,02h,08h,0Dh, and FFFFh.
0-3 Set corresponding bits in W_TXREQ_READ (0=No change, 1=Set)
4-15 Unknown (if any)
Firmware writes values 01h,02h,05h,08h,0Dh.
0 Send W_TXBUF_LOC1 (1=Transfer, if enabled in W_TXBUF_LOC1.Bit15)
1 Send W_TXBUF_CMD (1=Transfer, if enabled in W_TXBUF_CMD.Bit15)
2 Send W_TXBUF_LOC2 (1=Transfer, if enabled in W_TXBUF_LOC2.Bit15)
3 Send W_TXBUF_LOC3 (1=Transfer, if enabled in W_TXBUF_LOC3.Bit15)
4 Unknown (Beacon?) (always 1, except when cleared via W_POWERFORCE)
5-15 Unknown/Not used
Bit0-3 can be set/reset via W_TXREQ_SET/W_TXREQ_RESET. The setting in W_TXREQ_READ remains intact even after the transfer(s) have completed.
If more than one of the LOC1,2,3 bits is set, then LOC3 is transferred first, LOC1 last.
Beacons are transferred in every Beacon Timeslot (if enabled in W_TXBUF_BEACON.Bit15).
Bit0,2,3 are automatically reset upon IRQ14 (by hardware).
0 W_TXBUF_LOC1 (1=Requested Transfer busy, or not yet started at all)
1 W_TXBUF_CMD (1=Requested Transfer busy, or not yet started at all)
2 W_TXBUF_LOC2 (1=Requested Transfer busy, or not yet started at all)
3 W_TXBUF_LOC3 (1=Requested Transfer busy, or not yet started at all)
4 W_TXBUF_BEACON (1=Beacon Transfer busy)
5-15 Unknown (if any)
Busy bits. If all three W_TXBUF_LOC’s are sent, then it goes through values 0Dh,05h,01h,00h; ie. LOC3 is transferred first, LOC1 last. The register is updated upon IRQ01 (by hardware).
Bit4 is set only in Beacon Timeslots.
For LOC1-3, this register is updated at the end of a transfer (upon the IRQ01 request), if retries occur then it is updated only after the final retry.
For BEACON, this register is updated only if enabled in W_TXSTATCNT.Bit15, and only after successful transfers (since beacon errors result in infinite retries).
For CMD, this register is updated only if enabled in W_TXSTATCNT.Bit13,14).
Bit0/1 act similar to W_IF Bit1/3, however, the W_IF Bits are set after each transmit (including retries).
0 One (or more) Packet has Completed (1=Yes)
(No matter if successful, for that info see Bit1)
(No matter if ALL packets are done, for that info see Bit12-13)
1 Packet Failed (1=Error)
2-7 Unknown/Not used
8-11 Usually 0, ...but firmware is checking for values 03h,08h,0Bh
(gets set to 07h when transferred W_TXBUF_LOC1/2/3 did have Bit12=set)
(gets set to 00h otherwise)
(gets set to 03h after beacons ;if enabled in W_TXSTATCNT.Bit15)
(gets set to 08h after cmd's ;if enabled in W_TXSTATCNT.Bit14)
(gets set to 0Bh after cmd ack's ;if enabled in W_TXSTATCNT.Bit13)
(gets set to 04h after reply's ;if enabled in W_TXSTATCNT.Bit12)
12-13 Packet that updated W_TXSTAT (0=LOC1/BEACON/CMD/REPLY, 1=LOC2, 2=LOC3)
14-15 Unknown/Not used
No idea how to reset bit0/1 once when they are set?
0-11 Unknown (usually zero) (otherwise disables RXing multiplay REPLY's?)
12 Update W_TXSTAT=0401h and trigger IRQ01 after REPLY transmits (1=Yes)
13 Update W_TXSTAT=0B01h and trigger IRQ01 after CMD ACK transmits (1=Yes)
14 Update W_TXSTAT=0800h and trigger IRQ01 after CMD DATA transmits(1=Yes)
15 Update W_TXSTAT=0301h and trigger IRQ01 after BEACON transmits (1=Yes)
Note: LOC1..3 transmits are always updating W_TXSTAT and triggering IRQ01.
0 IEEE FC.Bit12 and Duration (0=Auto/whatever, 1=Manual/Wifi RAM)
1 IEEE Frame Check Sequence (0=Auto/FCS/CRC32, 1=Manual/Wifi RAM)
2 IEEE Sequence Control (0=Auto/W_TX_SEQNO, 1=Manual/Wifi RAM)
3-15 Always zero
Allows to disable automatic adjustments of the IEEE header and checksum.
Note: W_TX_SEQNO can be also disabled by W_TXBUF_LOCn.Bit13 and by TXHDR[04h].
0-11 Increments on IRQ07 (Transmit Start Interrupt)
12-15 Always zero
Also incremented shortly after IRQ12.
When enabled in W_TXBUF_LOCn.Bit13, this value replaces the upper 12bit of the IEEE Frame Header’s Sequence Control value (otherwise, when disabled, the original value in Wifi RAM is used, and, in that case, W_TX_SEQNO is NOT incremented).
Aside from W_TXBUF_LOCn.Bit13, other ways to disable W_TX_SEQNO are: Transmit Hardware Header entry TXHDR[04h], and W_TX_HDR_CNT.Bit2.
0 Always zero
1-12 Halfword Address in RAM for Writes via W_TXBUF_WR_DATA
13-15 Always zero
0-15 Data to be written to address specified in W_TXBUF_WR_ADDR
After writing to this register, W_TXBUF_WR_ADDR is automatically incremented by 2, and, if it gets equal to W_TXBUF_GAP, then it gets additonally incremented by W_TXBUF_GAPDISP*2.
0 Always zero
1-12 Halfword Address
13-15 Always zero
0-11 Halfword Offset (added to; if equal to W_TXBUF_GAP)
12-15 Always zero
Should be “0-write_buffer_size” (wrap from end to begin), or zero (no wrapping).
Caution: On the DS-Lite, after adding it to W_TXBUF_WR_ADDR, the W_TXBUF_GAPDISP setting is destroyed (reset to 0000h) by hardware. The original DS leaves W_TXBUF_GAPDISP intact.
Note: W_TXBUF_GAP and W_TXBUF_GAPDISP may be (not TOO probably) also used by transmits via W_TXBUF_LOCn and W_TXBUF_BEACON (not tested).
0-11 Halfword Address of TX Frame Header in RAM
12 For LOC1-3: When set, W_TXSTAT.bit8-10 are set to 07h after transfer
And, when set, the transferred frame-body gets messed up?
For BEACON: Unknown, no effect on W_TXSTAT
For CMD: Unknown, no effect on W_TXSTAT
13 IEEE Sequence Control (0=From W_TX_SEQNO, 1=Value in Wifi RAM)
For BEACON: Unknown (always uses W_TX_SEQNO) (no matter of bit13)
14 Unknown
15 Transfer Request (1=Request/Pending)
For LOC1..3 and CMD, Bit15 is automatically cleared after (or rather: during?) transfer (no matter if the transfer was successful). For Beacons, bit15 is kept unchanged since beacons are intended to be transferred repeatedly.
W_TXBUF_CMD.Bit15 can be set ONLY while W_CMD_COUNT is non-zero.
0 Disable LOC1 (0=No change, 1=Reset W_TXBUF_LOC1.Bit15)
1 Disable CMD (0=No change, 1=Reset W_TXBUF_CMD.Bit15)
2 Disable LOC2 (0=No change, 1=Reset W_TXBUF_LOC2.Bit15)
3 Disable LOC3 (0=No change, 1=Reset W_TXBUF_LOC3.Bit15)
4-5 Unknown/Not used
6 Disable REPLY2 (0=No change, 1=Reset W_TXBUF_REPLY2.Bit15)
7 Disable REPLY1 (0=No change, 1=Reset W_TXBUF_REPLY1.Bit15)
8-15 Unknown/Not used
Firmware writes values FFFFh, 40h, 02h, xxxx, 09h, 01h, 02h, C0h.
0-7 Location of TIM parameters within Beacon Frame Body
8-15 Not used/zero
Usually set to 15h, that assuming that preceding Frame Body content is: Timestamp(8), BeaconInterval(2), Capability(2), SuppRatesTagLenParams(4), ChannelTagLenParam(3), TimTagLen(2); so the value points to TimParams (ie. after TimTagLen).
0-11 Decremented on writes to W_TXBUF_WR_DATA
12-15 Always zero
Triggers IRQ08 when it reaches zero, and does then stay at zero (without further decrementing, and without generating further IRQs).
Note: Not affected by (accidental) reads from write-only W_TXBUF_WR_DATA.
Transmit errors occur on missing ACKs. The NDS hardware is automatically responding with an ACK when receiving a packet (if it has been addressed to the receipients W_MACADDR setting). And, when sending a packet, the NDS hardware is automatically checking for ACK responses.
The only exception are packets that are sent to group addresses (ie. Bit0 of the 48bit MAC address being set to “1”, eg. Beacons sent to FF:FF:FF:FF:FF:FF), the receipient(s) don’t need to respond to such packets, and the sender always passes okay without checking for ACKs.
Specifies the maximum number of retries on Transmit Errors (eg. 07h means one initial transmit, plus up to 7 retries, ie. max 8 transmits in total).
0-7 Retry Count (usually 07h)
8-15 Unknown (usually 07h)
The Retry Count value is decremented on each Error (unless it is already 00h). There’s no automatic reload, so W_TX_RETRYLIMIT should be reinitialized by software prior to each transmit (or, actually, there IS a reload?).
When sending multiple packets (by setting more than one bit with W_TXREQ_SET), then the first packet may eat-up all retries, leaving only a single try to the other packet(s).
0-7 TransmitErrorCount
8-15 Always zero
Increments on Transmit Errors. Automatically reset to zero after reading.
IRQ03 triggered when W_TX_ERR_COUNT is incremented (for NON-beacons ONLY).
IRQ05 triggered when W_TX_ERR_COUNT > 7Fh (happens INCLUDING for beacons).
Transmit Errors can be sensed via W_TX_ERR_COUNT, IRQ03, IRQ05, TX Hardware Header entry [00h], and W_TXSTAT.Bit1.
As the name says, W_TXBUF_BEACON is intended for sending Beacons to group addresses (which do not require to respond by ACKs). So, transmit errors would occur only when mis-using W_TXBUF_BEACON to send packets to individual addresses, but the W_TXBUF_BEACON error handling isn’t fully implemented:
First of, W_TX_RETRYLIMIT isn’t used, instead, W_TXBUF_BEACON errors will result in infinite retries.
Moreover, W_TXBUF_BEACON errors seem to increment W_TX_ERR_COUNT, but without generating IRQ03, however, IRQ05 is generated when W_TX_ERR_COUNT>7Fh.
The NDS transmit hardware seems to do little error checking on the packet headers. The only known error-checked part is byte [04h] in the TX hardware header (which must be 00h, 01h, or 02h). Aside from that, when sent to a group address, it is passing okay even with invalid IEEE type/subtypes, and even with Length/Rate entries set to zero. However, when sending such data to an individual address, the receiving NDS won’t respond by ACKs.
Received ACKs aren