<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.3dbrew.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=MarcusD</id>
	<title>3dbrew - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.3dbrew.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=MarcusD"/>
	<link rel="alternate" type="text/html" href="https://www.3dbrew.org/wiki/Special:Contributions/MarcusD"/>
	<updated>2026-04-25T02:14:57Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=23702</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=23702"/>
		<updated>2025-10-29T00:57:09Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Custom registers for controller 0xC7 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Internal battery temperature (in Celsius)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (integer part).&lt;br /&gt;
&lt;br /&gt;
Valid values are in range of 0 to 100 inclusive.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (fractional part).&lt;br /&gt;
&lt;br /&gt;
Seems to have a resolution of around 0.1% according to tests.&lt;br /&gt;
&lt;br /&gt;
To calculate battery charge percentage in full resolution:&lt;br /&gt;
 MCU[0x0B] + (MCU[0x0C] / 256.0F)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage.&lt;br /&gt;
&lt;br /&gt;
This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.&lt;br /&gt;
&lt;br /&gt;
To calculate system voltage in Volts:&lt;br /&gt;
 MCU[0x0D] * 5 / 256.0F&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: Accelerometer I2C read/write done [https://github.com/profi200/libn3ds/blob/083c8ffa3f56a49802fa74b6afe45a96820f0439/include/arm11/drivers/mcu_regmap.h#L124]&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Temporary playtime data in case console doesn&#039;t shut down gracefully, updated every 5 minutes&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code (determined from battery middle pin)&lt;br /&gt;
    0x00: Maxell (middle pin tied to GND)&lt;br /&gt;
      CTR-003 CTR-A-BP (old3DS)&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.&lt;br /&gt;
      SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: On-board battery slot NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD&lt;br /&gt;
  - 0xE - SHARP (TFT), found in 2DS only&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
    - LQ050B1LW10B&lt;br /&gt;
      - LQ = normal TFT&lt;br /&gt;
      - 050 = panel 5 inches diagonal&lt;br /&gt;
      - B = &amp;quot;other&amp;quot; display format&lt;br /&gt;
      - 1 = transmissive (backlight-compatible)&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 10 = model number&lt;br /&gt;
      - B = *unknown suffix*&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
    - LS035T7LE38P (top screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 035 = panel 3.5 inches diagonal&lt;br /&gt;
      - T = &amp;quot;other 16:9&amp;quot; (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - E = *unknown coating type*&lt;br /&gt;
      - 38 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
    - LS030Q7DW48P (bottom screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 030 = panel 3 inches diagonal&lt;br /&gt;
      - Q = QVGA (320x240)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - D = parallel RGB (unspecified, but it&#039;s known to be RGB888 for this display)&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 48 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| Factory protection&lt;br /&gt;
| 0x01&lt;br /&gt;
| When 0, not only the factory controls are write-protected, but also the factory key.&lt;br /&gt;
Set to 1 by default, which means that factory controls are enableable and writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - trim 8bpc to 6bpc (seems like color[1:0] &amp;lt;= color[7:6])&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 17 ==&lt;br /&gt;
&lt;br /&gt;
(Stub)&lt;br /&gt;
&lt;br /&gt;
Used by New 3DS for ZL, ZR, C stick&lt;br /&gt;
&lt;br /&gt;
This device do not use registers. After writing the address, read the next several bytes.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| Fixed 0x80&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| Buttons (ZL = 0x4, ZR = 0x2)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=23701</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=23701"/>
		<updated>2025-10-29T00:34:31Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Custom registers for controller 0xC7 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Internal battery temperature (in Celsius)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (integer part).&lt;br /&gt;
&lt;br /&gt;
Valid values are in range of 0 to 100 inclusive.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (fractional part).&lt;br /&gt;
&lt;br /&gt;
Seems to have a resolution of around 0.1% according to tests.&lt;br /&gt;
&lt;br /&gt;
To calculate battery charge percentage in full resolution:&lt;br /&gt;
 MCU[0x0B] + (MCU[0x0C] / 256.0F)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage.&lt;br /&gt;
&lt;br /&gt;
This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.&lt;br /&gt;
&lt;br /&gt;
To calculate system voltage in Volts:&lt;br /&gt;
 MCU[0x0D] * 5 / 256.0F&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: Accelerometer I2C read/write done [https://github.com/profi200/libn3ds/blob/083c8ffa3f56a49802fa74b6afe45a96820f0439/include/arm11/drivers/mcu_regmap.h#L124]&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Temporary playtime data in case console doesn&#039;t shut down gracefully, updated every 5 minutes&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code (determined from battery middle pin)&lt;br /&gt;
    0x00: Maxell (middle pin tied to GND)&lt;br /&gt;
      CTR-003 CTR-A-BP (old3DS)&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.&lt;br /&gt;
      SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: On-board battery slot NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD&lt;br /&gt;
  - 0xE - SHARP (TFT), found in 2DS only&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
    - LQ050B1LW10B&lt;br /&gt;
      - LQ = normal TFT&lt;br /&gt;
      - 050 = panel 5 inches diagonal&lt;br /&gt;
      - B = &amp;quot;other&amp;quot; display format&lt;br /&gt;
      - 1 = transmissive (backlight-compatible)&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 10 = model number&lt;br /&gt;
      - B = *unknown suffix*&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
    - LS035T7LE38P (top screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 035 = panel 3.5 inches diagonal&lt;br /&gt;
      - T = &amp;quot;other 16:9&amp;quot; (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - E = *unknown coating type*&lt;br /&gt;
      - 38 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
    - LS030Q7DW48P (bottom screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 030 = panel 3 inches diagonal&lt;br /&gt;
      - Q = QVGA (320x240)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - D = parallel RGB (unspecified, but it&#039;s known to be RGB888 for this display)&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 48 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - trim 8bpc to 6bpc (seems like color[1:0] &amp;lt;= color[7:6])&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 17 ==&lt;br /&gt;
&lt;br /&gt;
(Stub)&lt;br /&gt;
&lt;br /&gt;
Used by New 3DS for ZL, ZR, C stick&lt;br /&gt;
&lt;br /&gt;
This device do not use registers. After writing the address, read the next several bytes.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| Fixed 0x80&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| Buttons (ZL = 0x4, ZR = 0x2)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22785</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22785"/>
		<updated>2024-10-18T17:21:44Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add some &amp;quot;&amp;quot;&amp;quot;&amp;quot;low-hanging&amp;quot;&amp;quot;&amp;quot;&amp;quot; SD driver error code bits&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Here is the format of the numbers displayed on the error screen:&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: ffWWGGNN&lt;br /&gt;
  p3p2p1p0 p7p6p5p4&lt;br /&gt;
  sd_softE sd_hardE&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;: sleep switch state (2==MCU sleep switch closed, 1==GPIO sleep switch closed (very bad if this happens), 0==sleep switch open)&lt;br /&gt;
* &amp;lt;code&amp;gt;WW&amp;lt;/code&amp;gt;: NVRAM (WiFi Flash) FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;GG&amp;lt;/code&amp;gt;: ntrboot FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;NN&amp;lt;/code&amp;gt;: NAND header (NCSD) load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;p&amp;lt;N&amp;gt;&amp;lt;/code&amp;gt;: NAND FIRM partition load error code. Note the order of the partitions in the error code!&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_softE&amp;lt;/code&amp;gt;: software error (SD driver status bits, see one section lower)&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_hardE&amp;lt;/code&amp;gt;: hardware error (SD device status bits, see one section lower)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 0x1FFFE000 memory ===&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* s8[4] 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is WiFi Flash, +3 is sleep sensor state.&lt;br /&gt;
* s8[8] 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
* u32 0x1FFFE000+0x18: SD driver internal error bitfield&lt;br /&gt;
* u32 0x1FFFE000+0x1C: R1 status bits received from the SD device, AND-ed with 0xFDFF0080 if eMMC (NAND), otherwise 0xFDF90008 if SD.&lt;br /&gt;
&lt;br /&gt;
=== BootROM SD driver error bits ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| STATUS2: received cmd field does not match what was sent&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| STATUS2: received CRC does not match what was calculated&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| STATUS2: framing error, stop bit was not encountered&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| STATUS2: data was not received within the timeout period&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| STATUS2: RX FIFO overflow&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| STATUS2: TX FIFO overflow&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| STATUS2 (bit31): illegal access error (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| At least one error bit was set in the command reply from the SD device, or other unexpected state is reported.&lt;br /&gt;
|-&lt;br /&gt;
| 0x100&lt;br /&gt;
| An illegal command was received by the SD device (ILLEGAL_COMMAND bit set).&lt;br /&gt;
|-&lt;br /&gt;
| 0x200&lt;br /&gt;
| Timer-based timeout while waiting for SD device operations to finish.&lt;br /&gt;
|-&lt;br /&gt;
| 0x400&lt;br /&gt;
| Got a timer-based timeout during MMC initialization sequence.&lt;br /&gt;
|-&lt;br /&gt;
| 0x800&lt;br /&gt;
| ??? some sort of timeout&lt;br /&gt;
|-&lt;br /&gt;
| 0x8000&lt;br /&gt;
| Timeout while trying to perform AES operation on sector data&lt;br /&gt;
|-&lt;br /&gt;
| 0x80000&lt;br /&gt;
| Tried to perform AES operation while another AES operation is taking place&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== BootROM SD device error bits ===&lt;br /&gt;
These error codes are received directly from the device, and are in the same format as received in an R1 type reply.&lt;br /&gt;
&lt;br /&gt;
See [https://www.sdcard.org/downloads/pls/ SD Specifications Part 1 Physical Layer Simplified Specification] for the error bit list.&lt;br /&gt;
&lt;br /&gt;
=== BootROM Status Codes ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22784</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22784"/>
		<updated>2024-10-18T16:09:08Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Change indentation of bootrom error sections, as it makes more logical sense&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Here is the format of the numbers displayed on the error screen:&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: ffWWGGNN&lt;br /&gt;
  p3p2p1p0 p7p6p5p4&lt;br /&gt;
  sd_softE sd_hardE&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;: sleep switch state (2==MCU sleep switch closed, 1==GPIO sleep switch closed (very bad if this happens), 0==sleep switch open)&lt;br /&gt;
* &amp;lt;code&amp;gt;WW&amp;lt;/code&amp;gt;: NVRAM (WiFi Flash) FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;GG&amp;lt;/code&amp;gt;: ntrboot FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;NN&amp;lt;/code&amp;gt;: NAND header (NCSD) load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;p&amp;lt;N&amp;gt;&amp;lt;/code&amp;gt;: NAND FIRM partition load error code. Note the order of the partitions in the error code!&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_softE&amp;lt;/code&amp;gt;: software error (SD driver status bits, see one section lower)&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_hardE&amp;lt;/code&amp;gt;: hardware error (SD device status bits, see one section lower)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 0x1FFFE000 memory ===&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* s8[4] 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is WiFi Flash, +3 is sleep sensor state.&lt;br /&gt;
* s8[8] 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
* u32 0x1FFFE000+0x18: SD driver internal error bitfield&lt;br /&gt;
* u32 0x1FFFE000+0x1C: SD driver SD hardware status bits from the controller, AND-ed with 0xFDFF0080 if eMMC, and 0xFDF90008 if SD.&lt;br /&gt;
&lt;br /&gt;
=== BootROM Status Codes ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22783</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22783"/>
		<updated>2024-10-18T16:07:02Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Confirmed all bootrom fields to be accurate&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Here is the format of the numbers displayed on the error screen:&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: ffWWGGNN&lt;br /&gt;
  p3p2p1p0 p7p6p5p4&lt;br /&gt;
  sd_softE sd_hardE&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;: sleep switch state (2==MCU sleep switch closed, 1==GPIO sleep switch closed (very bad if this happens), 0==sleep switch open)&lt;br /&gt;
* &amp;lt;code&amp;gt;WW&amp;lt;/code&amp;gt;: NVRAM (WiFi Flash) FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;GG&amp;lt;/code&amp;gt;: ntrboot FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;NN&amp;lt;/code&amp;gt;: NAND header (NCSD) load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;p&amp;lt;N&amp;gt;&amp;lt;/code&amp;gt;: NAND FIRM partition load error code. Note the order of the partitions in the error code!&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_softE&amp;lt;/code&amp;gt;: software error (SD driver status bits, see one section lower)&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_hardE&amp;lt;/code&amp;gt;: hardware error (SD device status bits, see one section lower)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 0x1FFFE000 memory ==&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* s8[4] 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is WiFi Flash, +3 is sleep sensor state.&lt;br /&gt;
* s8[8] 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
* u32 0x1FFFE000+0x18: SD driver internal error bitfield&lt;br /&gt;
* u32 0x1FFFE000+0x1C: SD driver SD hardware status bits from the controller, AND-ed with 0xFDFF0080 if eMMC, and 0xFDF90008 if SD.&lt;br /&gt;
&lt;br /&gt;
== BootROM Status Codes ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22782</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22782"/>
		<updated>2024-10-18T12:33:55Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Document bootrom error screen format in a human-readable format&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Here is the format of the numbers displayed on the error screen:&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: ffWWGGNN&lt;br /&gt;
  p3p2p1p0 p7p6p5p4&lt;br /&gt;
  sd_softE sd_hardE&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;: sleep switch state (2==MCU sleep switch closed, 1==GPIO sleep switch closed (very bad if this happens), 0==sleep switch open)&lt;br /&gt;
* &amp;lt;code&amp;gt;WW&amp;lt;/code&amp;gt;: NVRAM (WiFi Flash) FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;GG&amp;lt;/code&amp;gt;: ntrboot FIRM load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;NN&amp;lt;/code&amp;gt;: NAND header (NCSD) load error code&lt;br /&gt;
* &amp;lt;code&amp;gt;p&amp;lt;N&amp;gt;&amp;lt;/code&amp;gt;: NAND FIRM partition load error code. Note the order of the partitions in the error code!&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_softE&amp;lt;/code&amp;gt;: software error (driver status bits, see one section lower)&lt;br /&gt;
* &amp;lt;code&amp;gt;sd_hardE&amp;lt;/code&amp;gt;: hardware error (SD controller status bits ? see one section lower)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 0x1FFFE000 memory ==&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* s8[4] 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is WiFi Flash, +3 is sleep sensor state.&lt;br /&gt;
* s8[8] 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
* u32 0x1FFFE000+0x18: SD driver internal error bitfield&lt;br /&gt;
* u32 0x1FFFE000+0x1C: SD driver SD hardware status bits from the controller, AND-ed with 0xFDFF0080 if eMMC, and 0xFDF90008 if SD.&lt;br /&gt;
&lt;br /&gt;
== BootROM Status Codes ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22781</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22781"/>
		<updated>2024-10-18T12:25:38Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add missing 1FFFE000 field info, and clean up some wording&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 0x1FFFE000 memory ==&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* s8[4] 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is WiFi Flash, +3 is sleep sensor state.&lt;br /&gt;
* s8[8] 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
* u32 0x1FFFE000+0x18: SD driver internal error bitfield&lt;br /&gt;
* u32 0x1FFFE000+0x1C: SD driver SD hardware status bits from the controller, AND-ed with 0xFDFF0080 if eMMC, and 0xFDF90008 if SD.&lt;br /&gt;
&lt;br /&gt;
== BootROM Status Codes ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22780</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22780"/>
		<updated>2024-10-18T12:20:07Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add unobtainable FD error, for the sake of 100% completeness&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 0x1FFFE000 memory ==&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* 8bit-entry-array 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is wifi-spiflash.&lt;br /&gt;
* ...&lt;br /&gt;
* 8bit-entry-array 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
== BootROM Status Codes ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD(-3)&lt;br /&gt;
| *unobtainable* SD driver initialization failed due to boot9 state not being initialized correctly (it&#039;s always initialized)&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22779</id>
		<title>Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Bootloader&amp;diff=22779"/>
		<updated>2024-10-18T06:41:42Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add more bootrom error codes, change complement representation to negative sign instead, and reword errors to be more understandable&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The bootloader is the binary code stored in the ARM9 and ARM11 boot ROMs and hence is ran when the 3DS is powered on. Its purpose is initializing hardware and loading the [[FIRM|system firmware]] from the internal [[Flash_Filesystem|NAND memory]]..&lt;br /&gt;
&lt;br /&gt;
Besides NATIVE_FIRM, the bootloader is also capable of booting other firmwares (such as TWL_FIRM and AGB_FIRM). However, this will result either in a Japanese error screen or a system shutdown, directly after FIRM Launching.&lt;br /&gt;
&lt;br /&gt;
== Boot ROM ==&lt;br /&gt;
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all 3DS consoles (3DS, 3DS XL, 2DS, New 3DS, New 3DS XL, New 2DS XL)&lt;br /&gt;
&lt;br /&gt;
== NAND FIRM boot ==&lt;br /&gt;
Boot9 is not hardcoded to only handle 2 FIRM partitions: it parses all 8 NCSD partitions for this. Boot9 will attempt to use every partition listed in the NCSD which is an actual FIRM partition, in the same order listed in the NCSD, until booting one of them succeeds. Among the not-yet-processed partitions, the FIRM which has the highest value at u32 firmhdr+4 will have a FIRM-boot attempted first. Since that value is normally 0x0, the order of FIRM-partition processing is normally identical to the order of the NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
Boot9 is hard-coded for using [[AES_Registers|AES]] keyslot 0x6 for NAND crypto.&lt;br /&gt;
&lt;br /&gt;
== Non-NAND FIRM boot ==&lt;br /&gt;
Boot9 can also boot from non-NAND. For this, a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normal key stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this.&lt;br /&gt;
&lt;br /&gt;
  CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header&lt;br /&gt;
  CTR_word[1] = outbufaddr;//FIRM section load addr&lt;br /&gt;
  CTR_word[2] = readsize;//FIRM section size&lt;br /&gt;
  CTR_word[3] = readsize;//FIRM section size&lt;br /&gt;
&lt;br /&gt;
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode).&lt;br /&gt;
&lt;br /&gt;
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway.&lt;br /&gt;
&lt;br /&gt;
For non-NAND booting, NCSD / FIRM-backup is not used.&lt;br /&gt;
&lt;br /&gt;
== SDMMC ==&lt;br /&gt;
&lt;br /&gt;
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and an SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header.&lt;br /&gt;
&lt;br /&gt;
== Boot9 RSA keyslots ==&lt;br /&gt;
&lt;br /&gt;
The following are initialized during main() startup, by initialize_rsakeyslots_pubk(). Each of these, for the ones which are actually set, have different keydata for retail/devunit.&lt;br /&gt;
* 0: Not set.&lt;br /&gt;
* 1: Used for the NAND FIRM signature.&lt;br /&gt;
* 2: Used for the non-NAND-FIRM signature.&lt;br /&gt;
* 3: Used for the NAND-NCSD FIRM signature.&lt;br /&gt;
&lt;br /&gt;
When FIRM loading is successful, initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk() is called, right before calling the final function in main(). Besides ITCM writing, this overwrites all 4 RSA keyslots with modulus + private-exponents loaded from boot9 data.&lt;br /&gt;
&lt;br /&gt;
initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk():&lt;br /&gt;
This initializes the 4 0x100-byte/0x200-byte chunks at 0x07ffb800+0x500(0x07ffbd00)/0x07ffb800+0x900(0x07ffc100). End address of the first section is 0x07ffc100(start addr of the second section), end address of the second section is 0x07ffc900. Hence, the first section total size is 0x400-bytes, while the second section total size is 0x800-bytes.&lt;br /&gt;
&lt;br /&gt;
These are initialized using via the boot9 data image, with ptrs from DTCM. Seperate keydata is used for retail/devunit.&lt;br /&gt;
&lt;br /&gt;
When initializing the first ITCM area: rsa_setkeyslot_privk() is called for all 4 RSA keyslots. The modulo for each one is also copied to (index*0x100) + 0x07ffb800 + 0x500. The private exponent is not copied into ITCM.&lt;br /&gt;
&lt;br /&gt;
The second ITCM area is initialized by copying 4 0x200-byte entries in a loop. These are RSA pubks+privks, which Boot9 doesn&#039;t use itself at all besides this copy loop.&lt;br /&gt;
&lt;br /&gt;
== Boot9 image data memory layout ==&lt;br /&gt;
0xffffb088 is the beginning of the boot9 image data section.&lt;br /&gt;
&lt;br /&gt;
* 0xffffb088 size 0x38-bytes: This is the array used during FIRM-section-loading for the memory-range blacklist for FIRM sections.&lt;br /&gt;
* 0xffffb0c0(end-addr of the above area) size 0x20-bytes: Unknown.&lt;br /&gt;
* 0xffffb0e0(end-addr of the above area) size 0x2f80-bytes: This is *all* of the keys stored in the image.&lt;br /&gt;
* 0xffffe060(end addr of the above key-area) size 0x230-bytes: This is the initial DTCM image @ 0xFFF00000, see below.&lt;br /&gt;
* 0xffffe290(DTCM_image_end) - {boot9 image end}: All-zero.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2f80-byte key-area at 0xffffb0e0:&lt;br /&gt;
* 0xffffb0e0 size 0x2600-bytes: This is the RSA key-data, see below.&lt;br /&gt;
* 0xffffd6e0(end-addr of the above area) size 0x40-bytes: This is the keydata used for crypting the entire OTP with keyslot 0x3f, used by main(). The first 0x20-bytes is for retail, the remaining 0x20-bytes starting at 0xffffd700 is for devunit. Chunk+0(retail=0xffffd6e0 devunit=0xffffd700) is the normalkey, chunk+0x10(retail=0xffffd6f0 devunit=0xffffd710) is the AES-IV.&lt;br /&gt;
* ...&lt;br /&gt;
* 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however, that code actually does the copy in two 0x40-bytes chunks.&lt;br /&gt;
* 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed.&lt;br /&gt;
* 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060.&lt;br /&gt;
&lt;br /&gt;
Layout of the 0x2600-byte RSA key-data at 0xffffb0e0:  &lt;br /&gt;
First 0x1300-bytes is for retail, the remaining 0x1300-bytes starting at 0xffffc3e0 is for devunit.&lt;br /&gt;
* +0x0 retail=0xffffb0e0 devunit=0xffffc3e0: RSA modulo for keyslot3, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x100 retail=0xffffb1e0 devunit=0xffffc4e0: RSA modulo for keyslot1, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x200 retail=0xffffb2e0 devunit=0xffffc5e0: RSA modulo for keyslot2, initialized by initialize_rsakeyslots_pubk().&lt;br /&gt;
* +0x300 size 0x200, retail=0xffffb3e0 devunit=0xffffc6e0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). This is for RSA-engine keyslot0 with initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), which also copies this modulo to the array starting at 0x07ffbd00.&lt;br /&gt;
* +0x500 size 0x200, retail=0xffffb5e0 devunit=0xffffc8e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0x700 size 0x200, retail=0xffffb7e0 devunit=0xffffcae0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x900 size 0x200, retail=0xffffb9e0 devunit=0xffffcce0: Used the same as the above block except for slot3.&lt;br /&gt;
* +0xb00 size 0x200, retail=0xffffbbe0 devunit=0xffffcee0: First 0x100-bytes is the RSA modulo, then the following 0x100-bytes is the RSA privk(private-exponent). The 0x200-bytes here is copied to slot0 in the array at 0x07ffc100 by initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk().&lt;br /&gt;
* +0xd00 size 0x200, retail=0xffffbde0 devunit=0xffffd0e0: Used the same as the above block except for slot1.&lt;br /&gt;
* +0xf00 size 0x200, retail=0xffffbfe0 devunit=0xffffd2e0: Used the same as the above block except for slot2.&lt;br /&gt;
* +0x1100 size 0x200, retail=0xffffc1e0 devunit=0xffffd4e0: Used the same as the above block except for slot3.&lt;br /&gt;
&lt;br /&gt;
== Boot9 DTCM layout ==&lt;br /&gt;
Most of this is just ptrs / other unknown data, not actual keys. However, there is an unknown 0x10-byte block @ +0x124(there&#039;s a ptr initialized for this block elsewhere).&lt;br /&gt;
&lt;br /&gt;
== Boot11 image data memory layout ==&lt;br /&gt;
* 0x0001817c..0x000181f4 size 0x78-bytes: This is the bootrom error screen font gfx data. This begins at the exact end-address of the crt0 code, the rest of the protected boot11 code begins at this end-address(0x000181f4). To extract the font gfx data from there, the 30 dwords at this address need to be converted to big endian. The correct resolution (when displayed as raw) is 32x30x1. The bootrom font looks very similar to [https://robey.lag.net/2010/01/23/tiny-monospace-font.html this font].&lt;br /&gt;
* 0x00019400 is the beginning of the boot11 data area, the first 8-bytes here are unknown.&lt;br /&gt;
* 0x00019408..0x0001b498 size 0x2090-bytes: This is the blowfish keydata which gets copied to arm9itcm_twlkeydata+0x3e0 later.&lt;br /&gt;
* 0x0001c498..0x0001c4f8 size 0x60-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x380.&lt;br /&gt;
* 0x0001c4f8..0x0001c538 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x340.&lt;br /&gt;
* 0x0001c538..0x0001c578 size 0x40-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x300.&lt;br /&gt;
* 0x0001c578..0x0001c5f8 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x280.&lt;br /&gt;
* 0x0001c5f8..0x0001c678 size 0x80-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x0.&lt;br /&gt;
* 0x0001c678..0x0001c878 size 0x200-bytes: This is the data which eventually gets copied to arm9itcm_twlkeydata+0x80.&lt;br /&gt;
* 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm.&lt;br /&gt;
* 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom.&lt;br /&gt;
&lt;br /&gt;
== AES keys ==&lt;br /&gt;
See the Tools section for how Boot9 initializes the keyslots.&lt;br /&gt;
&lt;br /&gt;
See also [[AES_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
For an issue with console-unique key-init, see [[OTP_Registers|here]].&lt;br /&gt;
&lt;br /&gt;
== BootROM Errors ==&lt;br /&gt;
Sample error-screen(where firm0+firm1 RSA signatures were corrupted):&lt;br /&gt;
&lt;br /&gt;
  BOOTROM 8046&lt;br /&gt;
  ERRCODE: 00F800FF&lt;br /&gt;
  DEDEFFFF FFFFFFFF&lt;br /&gt;
  00000000 00000000&lt;br /&gt;
&lt;br /&gt;
* 1st line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;BOOTROM %X&amp;quot;, 0x8046);//This last param comes from the .pool.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 2nd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;ERRCODE:    %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 3rd line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;, *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
* 4th line is: &amp;lt;code&amp;gt;print_string(..., &amp;quot;%08X %08X&amp;quot;,*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 0x1FFFE000 memory ==&lt;br /&gt;
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM.&lt;br /&gt;
&lt;br /&gt;
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be &#039;&#039;only&#039;&#039; accessed by the boot11 function initializing those words.&lt;br /&gt;
&lt;br /&gt;
* u32 0x1FFFE000+0: ARM11 MPCore &amp;quot;Cycle Counter Register (CCNT)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+4: ARM11 MPCore &amp;quot;Count Register 0 (PMN0)&amp;quot;.&lt;br /&gt;
* u32 0x1FFFE000+8: ARM11 MPCore &amp;quot;Count Register 1 (PMN0)&amp;quot;.&lt;br /&gt;
* 8bit-entry-array 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND, +1 is NTRCARD and +2 is wifi-spiflash.&lt;br /&gt;
* ...&lt;br /&gt;
* 8bit-entry-array 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions.&lt;br /&gt;
&lt;br /&gt;
== BootROM Status Codes ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| Device was not considered to be loaded.&lt;br /&gt;
Can also indicate success, but not necessarily when seeing the blue bootrom error screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF(-1)&lt;br /&gt;
| Partition skipped due to it not being a FIRM partition (partition fs type isn&#039;t 0x3 and partition fs crypt-type isn&#039;t 0x2).&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE(-2)&lt;br /&gt;
| Device initialization failed due to it missing or malfunctioning&lt;br /&gt;
|-&lt;br /&gt;
| 0xF8(-8)&lt;br /&gt;
| The FIRM header magic is not matching &amp;quot;FIRM&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF7(-9)&lt;br /&gt;
| FIRM image loading got skipped due to already having found an equal or higher priority (firmhdr+4) FIRM to load.&lt;br /&gt;
|-&lt;br /&gt;
| 0xEF(-17)&lt;br /&gt;
| Failed to load NCSD header from NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0xEE(-18)&lt;br /&gt;
| NCSD header magic is not &amp;quot;NCSD&amp;quot;, or NCSD header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDF(-33)&lt;br /&gt;
| Failed to read FIRM header from device.&lt;br /&gt;
|-&lt;br /&gt;
| 0xDE(-34)&lt;br /&gt;
| FIRM header magic is not &amp;quot;FIRM&amp;quot;, or FIRM header RSA verification failed.&lt;br /&gt;
|-&lt;br /&gt;
| 0xCF(-49)&lt;br /&gt;
| FIRM section loading failed for any of these reasons:&lt;br /&gt;
* FIRM section load address blacklist got tripped&lt;br /&gt;
* Failed to read FIRM section data into memory&lt;br /&gt;
* FIRM section hash verification failed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Boot9 startup ==&lt;br /&gt;
&lt;br /&gt;
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0:&lt;br /&gt;
* Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero.&lt;br /&gt;
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch.&lt;br /&gt;
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode.&lt;br /&gt;
* Then L_ffff80cc/mpu_init() is called.&lt;br /&gt;
* Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000.&lt;br /&gt;
* Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn&#039;t do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared.&lt;br /&gt;
* Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0.&lt;br /&gt;
* Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction.&lt;br /&gt;
&lt;br /&gt;
mpu_init():&lt;br /&gt;
* Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer.&lt;br /&gt;
* Then the 8 [[Memory_layout|MPU]] memregions are initialized.&lt;br /&gt;
* ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000).&lt;br /&gt;
* DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000).&lt;br /&gt;
* Then instruction cachable and data cachable/bufferable bits for the MPU regions are setup.&lt;br /&gt;
* Then the instruction/data access permissions for the MPU regions are setup.&lt;br /&gt;
* Lastly bitmask 0x0005707d is orred in the cp15 control register.&lt;br /&gt;
&lt;br /&gt;
== Boot9 main() ==&lt;br /&gt;
&lt;br /&gt;
  The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset().&lt;br /&gt;
  Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed.&lt;br /&gt;
  Then AES keyslot 0x3f is selected.&lt;br /&gt;
  When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800).&lt;br /&gt;
  Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70);&lt;br /&gt;
  Then sp+4 size 0x100 is cleared to zero.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  NAND firm-boot code-block is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6).&lt;br /&gt;
  	Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  	Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10).&lt;br /&gt;
  &lt;br /&gt;
  	NAND_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xc.&lt;br /&gt;
  	Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called.&lt;br /&gt;
  	Then LT_ffff5644() is called.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far.&lt;br /&gt;
  {&lt;br /&gt;
  	timer_updatestoredstate() is called.&lt;br /&gt;
  &lt;br /&gt;
  	Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND.&lt;br /&gt;
  	Then fsdriver_setup_wififlash() is called.&lt;br /&gt;
  	Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval.&lt;br /&gt;
  &lt;br /&gt;
  	SPIFLASH_BOOTEND:&lt;br /&gt;
  	Then the statuscode variable is written to u8 0x1fffe000+0xe.&lt;br /&gt;
  	Then timer_updatestoredstate() is called.&lt;br /&gt;
  	When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_END:&lt;br /&gt;
  Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE.&lt;br /&gt;
  After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_FAILURE:&lt;br /&gt;
  Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470.&lt;br /&gt;
  Then it continues to FIRMLOAD_EXIT.&lt;br /&gt;
  &lt;br /&gt;
  FIRMLOAD_EXIT:&lt;br /&gt;
  Here firmboot() is called, which should never return. The instruction after this bl is a call for panic().&lt;br /&gt;
&lt;br /&gt;
== Boot11 ==&lt;br /&gt;
&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
main():&lt;br /&gt;
  LT_1263c();&lt;br /&gt;
  ...&lt;br /&gt;
  LT_13944()&lt;br /&gt;
  ...&lt;br /&gt;
  pxi_init();&lt;br /&gt;
  initializefuncptr_firmboot_start(firmbootbegin_funcptr);&lt;br /&gt;
  firmboot();&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
LT_12220/initializefuncptr_firmboot_start&lt;br /&gt;
  inr0=funcptr&lt;br /&gt;
  This writes inr0 to address 0x1ffe8028, then returns.&lt;br /&gt;
  This initializes the funcptr which firmboot() can call after the very first func-call.&lt;br /&gt;
&lt;br /&gt;
LT_13944&lt;br /&gt;
  if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0)&lt;br /&gt;
  {&lt;br /&gt;
  	return (*((u8*)0x10147000) &amp;gt;&amp;gt; 4) &amp;amp; 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails.&lt;br /&gt;
  }&lt;br /&gt;
  Here it basically does &amp;quot;return &amp;lt;byte loaded from sp+0&amp;gt; ^ 0x2&amp;quot;. Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned.&lt;br /&gt;
&lt;br /&gt;
LT_12454/firmboot&lt;br /&gt;
  This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses.&lt;br /&gt;
  ptr = firmboot_loadentrypoint11();&lt;br /&gt;
  funcptr = *(0x1ffe8028);&lt;br /&gt;
  if(funcptr)funcptr(ptr);&lt;br /&gt;
  LT_11ffc(ptr);&lt;br /&gt;
  return;&lt;br /&gt;
&lt;br /&gt;
== Boot Procedure ==&lt;br /&gt;
&lt;br /&gt;
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution.&lt;br /&gt;
* &amp;lt;= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running.&lt;br /&gt;
**The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc.&lt;br /&gt;
**The [[Process_Manager_Services|PM]] module launches [[NS]].&lt;br /&gt;
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles.&lt;br /&gt;
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process.&lt;br /&gt;
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules.&lt;br /&gt;
&lt;br /&gt;
* 4 seconds - the LCD screens are initialized.&lt;br /&gt;
&lt;br /&gt;
* 7 seconds - [[Home Menu]] is fully initialized/loaded.&lt;br /&gt;
&lt;br /&gt;
== NAND Reads during Boot ==&lt;br /&gt;
During a successful boot on 6.x, the bootloader (and firm) reads the following sectors from NAND (in this order):&lt;br /&gt;
 00000000 (NCSD Partition Table)&lt;br /&gt;
 &lt;br /&gt;
 Only verify &#039;FIRM&#039; magic? (A second Header-read will be attempted even if everything except the magic is 0xFF...)&lt;br /&gt;
 0B130000 (FIRM Partition)&lt;br /&gt;
 0B530000 (Secondary FIRM Partition)&lt;br /&gt;
 &lt;br /&gt;
 Verify RSA signature and parse Header:&lt;br /&gt;
 0B130000 (FIRM: Header)&lt;br /&gt;
 0B130200 (FIRM: Section 1)&lt;br /&gt;
 0B163E00 (FIRM: Section 2)&lt;br /&gt;
 0B193E00 (FIRM: Section 3)&lt;br /&gt;
 &lt;br /&gt;
 00013000 .. Below is probably NATIVE_FIRM booting ..&lt;br /&gt;
 00014000&lt;br /&gt;
 00015000&lt;br /&gt;
 00016000&lt;br /&gt;
 00017000&lt;br /&gt;
 &lt;br /&gt;
 09011A00&lt;br /&gt;
 09011C00&lt;br /&gt;
 09012000&lt;br /&gt;
 09012400&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Error Codes ==&lt;br /&gt;
When the 3DS does not find the NAND chip, the following error is displayed:&lt;br /&gt;
&lt;br /&gt;
[[Image:CTR_Bootrom_Error.jpg|240px]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Error&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000200 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Error when having SD-card reader connected to NAND during boot.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE 00000000 00000000 00000400 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND not found error (?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000080 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT1 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00800000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT2 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FE FFFFFFFF FFFFFFFF 00000005 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| NAND error when DAT3 was used as DAT0.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF F8F8FFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (failed signature checks).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF DEDEFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt (possibly related to certain flags missing?)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800FF CFCFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Both the firm0 and firm1 partitions are corrupt&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;00F800EE FFFFFFFF FFFFFFFF 00000000 00000000&amp;lt;/tt&amp;gt;&lt;br /&gt;
| [[NCSD]] header in sector 0 is corrupt (failed signature check).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hardware Failure indications ==&lt;br /&gt;
When a hardware failure is detected, a LED indicator is shown.&lt;br /&gt;
&lt;br /&gt;
If you replace a Nintendo 3ds console&#039;s screen with another Nintendo 3ds model&#039;s screen, the console powers on, the screens stays black, but after a minute, the wireless LED blinks four times, stays on for a second, then powers off. The blue led stays on, though.&lt;br /&gt;
&lt;br /&gt;
We do not have much information about this as usually, when a hardware failure is detected, the console crashes or powers off immediately.&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
* [https://github.com/yellows8/boot9_tools boot9_tools]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22767</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22767"/>
		<updated>2024-09-27T17:50:58Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Clarity bit6 alternative mode, clean up interlacing language, elaborate how fb interlacing works, and minor cleanup on the text below the fb format register table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00000&lt;br /&gt;
| 0x10400000&lt;br /&gt;
| 4&lt;br /&gt;
| Hardware ID&lt;br /&gt;
| Bit2: new model&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)&lt;br /&gt;
 - Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HStart&lt;br /&gt;
| Determines when the image is going to be displayed in the visible region (register 0x60).&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HBR&lt;br /&gt;
| Right border start(?). Does nothing.&lt;br /&gt;
&lt;br /&gt;
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HPF&lt;br /&gt;
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.&lt;br /&gt;
&lt;br /&gt;
Unknown why, but a single dot of red is displayed before entering this mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (450 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| See [[#Framebuffer_format|framebuffer format]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| [[#Framebuffer_color_formats|Color format]]&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer interlacing mode&lt;br /&gt;
&lt;br /&gt;
 0 - A  (no interlacing)&lt;br /&gt;
 1 - AA (scanline doubling)&lt;br /&gt;
 2 - AB (interlace enable)&lt;br /&gt;
 3 - BA (same as above, but the fields are inverted)&lt;br /&gt;
&lt;br /&gt;
In AB and BA interlace modes, a scanline from each framebuffer is output in an alternating manner. In AB mode, Framebuffer A is output on the frist display scanline. Similarly, in BA mode, Framebuffer B gets output to the first display scanline.&lt;br /&gt;
&lt;br /&gt;
The way AB and BA modes work, is that a scanline is output, the framebuffer stride value is added to the internal scanline pointer value, and the other framebuffer is selected. And this alternates until the end of the draw region.&lt;br /&gt;
&lt;br /&gt;
AA interlacing works like AB interlacing, except both internal framebuffer pointers are set to the Framebuffer A pointer value.&lt;br /&gt;
&lt;br /&gt;
In A mode (no interlacing), it doesn&#039;t switch to the other framebuffer at the end of outpuitting a scanline to the display.&lt;br /&gt;
&lt;br /&gt;
Bottom screen has this set to 0 (A mode, no interlacing) at all times.  &lt;br /&gt;
Top screen uses AB interlacing in 3D mode (with 3D slider enabled), and A mode (no interlacing) in 2D mode.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Alternative pixel output mode*&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| DMA size&lt;br /&gt;
&lt;br /&gt;
 0 -  4 FCRAM words (32 bytes)&lt;br /&gt;
 1 -  8 FCRAM words (64 bytes)&lt;br /&gt;
 2 - 16 FCRAM words (128 bytes)&lt;br /&gt;
 3 - ???&lt;br /&gt;
&lt;br /&gt;
FCRAM doesn&#039;t support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.&lt;br /&gt;
|-&lt;br /&gt;
| 31-16&lt;br /&gt;
| Unknown&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; The weird thing about bit6, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing pixel/column doubling). However on the top screen, it does scanline doubling instead.  &lt;br /&gt;
Most likely the top screen receives two pixels at once per clock unit, outputting two scanlines simultaneously.&lt;br /&gt;
&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy (3D) to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and alternative mode is disabled (bit6=0), the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled (bit5=1, bit6=0), gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.  &lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or by skipping lines (both framebuffers are treated as 240x800).&lt;br /&gt;
&lt;br /&gt;
If only alternative mode is enabled (bit5=0, bit6=1), gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22619</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22619"/>
		<updated>2024-09-06T11:38:24Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Update info about battery middle pin and temperature reading&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Internal battery temperature (in Celsius)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (integer part).&lt;br /&gt;
&lt;br /&gt;
Valid values are in range of 0 to 100 inclusive.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (fractional part).&lt;br /&gt;
&lt;br /&gt;
Seems to have a resolution of around 0.1% according to tests.&lt;br /&gt;
&lt;br /&gt;
To calculate battery charge percentage in full resolution:&lt;br /&gt;
 MCU[0x0B] + (MCU[0x0C] / 256.0F)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage.&lt;br /&gt;
&lt;br /&gt;
This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.&lt;br /&gt;
&lt;br /&gt;
To calculate system voltage in Volts:&lt;br /&gt;
 MCU[0x0D] * 5 / 256.0F&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: Accelerometer I2C read/write done [https://github.com/profi200/libn3ds/blob/083c8ffa3f56a49802fa74b6afe45a96820f0439/include/arm11/drivers/mcu_regmap.h#L124]&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Temporary playtime data in case console doesn&#039;t shut down gracefully, updated every 5 minutes&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code (determined from battery middle pin)&lt;br /&gt;
    0x00: Maxell (middle pin tied to GND)&lt;br /&gt;
      CTR-003 CTR-A-BP (old3DS)&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.&lt;br /&gt;
      SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: On-board battery slot NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD&lt;br /&gt;
  - 0xE - SHARP (TFT), found in 2DS only&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
    - LQ050B1LW10B&lt;br /&gt;
      - LQ = normal TFT&lt;br /&gt;
      - 050 = panel 5 inches diagonal&lt;br /&gt;
      - B = &amp;quot;other&amp;quot; display format&lt;br /&gt;
      - 1 = transmissive (backlight-compatible)&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 10 = model number&lt;br /&gt;
      - B = *unknown suffix*&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
    - LS035T7LE38P (top screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 035 = panel 3.5 inches diagonal&lt;br /&gt;
      - T = &amp;quot;other 16:9&amp;quot; (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - E = *unknown coating type*&lt;br /&gt;
      - 38 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
    - LS030Q7DW48P (bottom screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 030 = panel 3 inches diagonal&lt;br /&gt;
      - Q = QVGA (320x240)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - D = parallel RGB (unspecified, but it&#039;s known to be RGB888 for this display)&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 48 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 17 ==&lt;br /&gt;
&lt;br /&gt;
(Stub)&lt;br /&gt;
&lt;br /&gt;
Used by New 3DS for ZL, ZR, C stick&lt;br /&gt;
&lt;br /&gt;
This device do not use registers. After writing the address, read the next several bytes.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| Fixed 0x80&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| Buttons (ZL = 0x4, ZR = 0x2)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22586</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22586"/>
		<updated>2024-06-22T16:15:35Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add and fix MCU-related battery and voltage info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Internal battery temperature (in Celsius)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (integer part).&lt;br /&gt;
&lt;br /&gt;
Valid values are in range of 0 to 100 inclusive.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage (fractional part).&lt;br /&gt;
&lt;br /&gt;
Seems to have a resolution of around 0.1% according to tests.&lt;br /&gt;
&lt;br /&gt;
To calculate battery charge percentage in full resolution:&lt;br /&gt;
 MCU[0x0B] + (MCU[0x0C] / 256.0F)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage.&lt;br /&gt;
&lt;br /&gt;
This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.&lt;br /&gt;
&lt;br /&gt;
To calculate system voltage in Volts:&lt;br /&gt;
 MCU[0x0D] * 5 / 256.0F&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: Accelerometer I2C read/write done [https://github.com/profi200/libn3ds/blob/083c8ffa3f56a49802fa74b6afe45a96820f0439/include/arm11/drivers/mcu_regmap.h#L124]&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Temporary playtime data in case console doesn&#039;t shut down gracefully, updated every 5 minutes&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
    0x00: Maxell&lt;br /&gt;
      CTR-003 CTR-A-BP (old3DS)&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
      CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.&lt;br /&gt;
      SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: Battery middle terminal NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD&lt;br /&gt;
  - 0xE - SHARP (TFT), found in 2DS only&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
    - LQ050B1LW10B&lt;br /&gt;
      - LQ = normal TFT&lt;br /&gt;
      - 050 = panel 5 inches diagonal&lt;br /&gt;
      - B = &amp;quot;other&amp;quot; display format&lt;br /&gt;
      - 1 = transmissive (backlight-compatible)&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 10 = model number&lt;br /&gt;
      - B = *unknown suffix*&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
    - LS035T7LE38P (top screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 035 = panel 3.5 inches diagonal&lt;br /&gt;
      - T = &amp;quot;other 16:9&amp;quot; (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - E = *unknown coating type*&lt;br /&gt;
      - 38 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
    - LS030Q7DW48P (bottom screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 030 = panel 3 inches diagonal&lt;br /&gt;
      - Q = QVGA (320x240)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - D = parallel RGB (unspecified, but it&#039;s known to be RGB888 for this display)&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 48 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 17 ==&lt;br /&gt;
&lt;br /&gt;
(Stub)&lt;br /&gt;
&lt;br /&gt;
Used by New 3DS for ZL, ZR, C stick&lt;br /&gt;
&lt;br /&gt;
This device do not use registers. After writing the address, read the next several bytes.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| Fixed 0x80&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| Buttons (ZL = 0x4, ZR = 0x2)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22516</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22516"/>
		<updated>2024-03-14T13:22:38Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Added some more display info. TODO: move the part number and its decoding somewhere else&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD&lt;br /&gt;
  - 0xE - SHARP (TFT), found in 2DS only&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
    - LQ050B1LW10B&lt;br /&gt;
      - LQ = normal TFT&lt;br /&gt;
      - 050 = panel 5 inches diagonal&lt;br /&gt;
      - B = &amp;quot;other&amp;quot; display format&lt;br /&gt;
      - 1 = transmissive (backlight-compatible)&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 10 = model number&lt;br /&gt;
      - B = *unknown suffix*&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
    - LS035T7LE38P (top screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 035 = panel 3.5 inches diagonal&lt;br /&gt;
      - T = &amp;quot;other 16:9&amp;quot; (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - L = LVDS&lt;br /&gt;
      - E = *unknown coating type*&lt;br /&gt;
      - 38 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
    - LS030Q7DW48P (bottom screen)&lt;br /&gt;
      - LS = TFT (LTPS or SI-TFT ?)&lt;br /&gt;
      - 030 = panel 3 inches diagonal&lt;br /&gt;
      - Q = QVGA (320x240)&lt;br /&gt;
      - 7 = *unknown backing type*&lt;br /&gt;
      - D = parallel RGB (unspecified, but it&#039;s known to be RGB888 for this display)&lt;br /&gt;
      - W = *unknown coating type*&lt;br /&gt;
      - 48 = model number&lt;br /&gt;
      - P = *unknow suffix*&lt;br /&gt;
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22425</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22425"/>
		<updated>2023-10-28T16:54:11Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: finally correct this awful misinformation. also make the formatting slightly better&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit1: ShellState&lt;br /&gt;
  bit3: AdapterState&lt;br /&gt;
  bit4: BatteryChargeState&lt;br /&gt;
  bit5: Bottom screen backlight on&lt;br /&gt;
  bit6: Top screen backlight on&lt;br /&gt;
  bit7: LCD panel voltage on&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.&lt;br /&gt;
  bit0: LCD panel voltage off&lt;br /&gt;
  bit1: LCD panel voltage on&lt;br /&gt;
  bit2: Bottom screen backlight off&lt;br /&gt;
  bit3: Bottom screen backlight on&lt;br /&gt;
  bit4: Top screen backlight off&lt;br /&gt;
  bit5: Top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22408</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22408"/>
		<updated>2023-10-15T21:45:07Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: adjust spacing so it doesn&amp;#039;t look janky&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)&lt;br /&gt;
 - Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HStart&lt;br /&gt;
| Determines when the image is going to be displayed in the visible region (register 0x60).&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HBR&lt;br /&gt;
| Right border start(?). Does nothing.&lt;br /&gt;
&lt;br /&gt;
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HPF&lt;br /&gt;
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.&lt;br /&gt;
&lt;br /&gt;
Unknown why, but a single dot of red is displayed before entering this mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| See [[#Framebuffer_format|framebuffer format]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| [[#Framebuffer_color_formats|Color format]]&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (framebuffer interleave config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, so framebuffer A is interleaved with itself)&lt;br /&gt;
 2 - AB (interleave framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| DMA size&lt;br /&gt;
&lt;br /&gt;
 0 -  4 words (32 bytes)&lt;br /&gt;
 1 -  8 words (64 bytes)&lt;br /&gt;
 2 - 16 words (128 bytes)&lt;br /&gt;
 3 - ???&lt;br /&gt;
&lt;br /&gt;
FCRAM doesn&#039;t support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.&lt;br /&gt;
|-&lt;br /&gt;
| 31-16&lt;br /&gt;
| Unknown&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22407</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22407"/>
		<updated>2023-10-15T21:43:21Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: progressive scan interlacing == interleaving&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)&lt;br /&gt;
 - Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HStart&lt;br /&gt;
| Determines when the image is going to be displayed in the visible region (register 0x60).&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HBR&lt;br /&gt;
| Right border start(?). Does nothing.&lt;br /&gt;
&lt;br /&gt;
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HPF&lt;br /&gt;
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.&lt;br /&gt;
&lt;br /&gt;
Unknown why, but a single dot of red is displayed before entering this mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| See [[#Framebuffer_format|framebuffer format]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| [[#Framebuffer_color_formats|Color format]]&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (framebuffer interleave config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, so framebuffer A is interleaved with itself)&lt;br /&gt;
 2 - AB (interleave framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| DMA size&lt;br /&gt;
&lt;br /&gt;
 0 - 4 words (32 bytes)&lt;br /&gt;
 1 - 8 words (64 bytes)&lt;br /&gt;
 2 - 16 words (128 bytes)&lt;br /&gt;
 3 - ???&lt;br /&gt;
&lt;br /&gt;
FCRAM doesn&#039;t support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.&lt;br /&gt;
|-&lt;br /&gt;
| 31-16&lt;br /&gt;
| Unknown&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22406</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=22406"/>
		<updated>2023-10-15T20:10:25Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: oops, I used RGB565 for testing, but used RGBA8888 for calculation, burst size of 256bytes doesn&amp;#039;t make sense for FCRAM anyway&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)&lt;br /&gt;
 - Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HStart&lt;br /&gt;
| Determines when the image is going to be displayed in the visible region (register 0x60).&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HBR&lt;br /&gt;
| Right border start(?). Does nothing.&lt;br /&gt;
&lt;br /&gt;
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HPF&lt;br /&gt;
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.&lt;br /&gt;
&lt;br /&gt;
Unknown why, but a single dot of red is displayed before entering this mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| See [[#Framebuffer_format|framebuffer format]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| [[#Framebuffer_color_formats|Color format]]&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| DMA size&lt;br /&gt;
&lt;br /&gt;
 0 - 4 words (32 bytes)&lt;br /&gt;
 1 - 8 words (64 bytes)&lt;br /&gt;
 2 - 16 words (128 bytes)&lt;br /&gt;
 3 - ???&lt;br /&gt;
&lt;br /&gt;
FCRAM doesn&#039;t support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.&lt;br /&gt;
|-&lt;br /&gt;
| 31-16&lt;br /&gt;
| Unknown&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22385</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22385"/>
		<updated>2023-10-05T13:15:26Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Custom registers for controller 0xE1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Analog curve top&lt;br /&gt;
| rowspan=2 | &lt;br /&gt;
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.&lt;br /&gt;
&lt;br /&gt;
Pair 1:&lt;br /&gt;
 byte 00 (0xFF) - ???&lt;br /&gt;
 byte 01 (0xFF) - ???&lt;br /&gt;
 byte 02 (0xFF) - ???&lt;br /&gt;
 byte 03 (0xFF) - ???&lt;br /&gt;
 byte 04 (0x3F) - ???&lt;br /&gt;
 byte 05 (0x3F) - ???&lt;br /&gt;
 byte 06 (0x3F) - ???&lt;br /&gt;
 byte 07 (0x3F) - ???&lt;br /&gt;
 byte 08 (0x3F) - ???&lt;br /&gt;
 byte 09 (0x3F) - ???&lt;br /&gt;
&lt;br /&gt;
Part 2:&lt;br /&gt;
 byte 10 (0xFF) - ???&lt;br /&gt;
 byte 11 (0xFF) - ???&lt;br /&gt;
 byte 12 (0xFF) - ???&lt;br /&gt;
 byte 13 (0xFF) - ???&lt;br /&gt;
 byte 14 (0x3F) - ???&lt;br /&gt;
 byte 15 (0x3F) - ???&lt;br /&gt;
 byte 16 (0x3F) - ???&lt;br /&gt;
 byte 17 (0x3F) - ???&lt;br /&gt;
 byte 18 (0x3F) - ???&lt;br /&gt;
 byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Analog curve bottom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22384</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22384"/>
		<updated>2023-10-05T12:50:10Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Custom registers for controller 0xE1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown. This applies to the whole display panel.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform&lt;br /&gt;
| 0x33&lt;br /&gt;
| &lt;br /&gt;
 bit0 - top half horizontal flip&lt;br /&gt;
 bit1 - top half red-blue swap&lt;br /&gt;
 bit4 - bottom half horizontal flip&lt;br /&gt;
 bit5 - bottom half red-blue swap&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22383</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22383"/>
		<updated>2023-10-05T12:00:18Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Custom registers for controller 0xC7 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Color curve red&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen&lt;br /&gt;
&lt;br /&gt;
Positive:&lt;br /&gt;
 - byte 00 (0xFF) - ???&lt;br /&gt;
 - byte 01 (0xFF) - ???&lt;br /&gt;
 - byte 02 (0x3F) - ???&lt;br /&gt;
 - byte 03 (0x3F) - ???&lt;br /&gt;
 - byte 04 (0x3F) - ???&lt;br /&gt;
 - byte 05 (0x3F) - ???&lt;br /&gt;
 - byte 06 (0x3F) - ???&lt;br /&gt;
 - byte 07 (0x3F) - ???&lt;br /&gt;
 - byte 08 (0x3F) - ???&lt;br /&gt;
 - byte 09 (0x3F) - ???&lt;br /&gt;
 &lt;br /&gt;
Negative:&lt;br /&gt;
 - byte 10 (0xFF) - ???&lt;br /&gt;
 - byte 11 (0xFF) - ???&lt;br /&gt;
 - byte 12 (0x3F) - ???&lt;br /&gt;
 - byte 13 (0x3F) - ???&lt;br /&gt;
 - byte 14 (0x3F) - ???&lt;br /&gt;
 - byte 15 (0x3F) - ???&lt;br /&gt;
 - byte 16 (0x3F) - ???&lt;br /&gt;
 - byte 17 (0x3F) - ???&lt;br /&gt;
 - byte 18 (0x3F) - ???&lt;br /&gt;
 - byte 19 (0x3F) - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Color curve green&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Color curve blue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22382</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22382"/>
		<updated>2023-10-05T10:15:20Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add more factory regs to controller 0xC7&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers for unlock register 0x03:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Image control?&lt;br /&gt;
| 0xD7&lt;br /&gt;
| Most bits are unknown.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color invert&lt;br /&gt;
 bit1 - slight gamma increase&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image transform?&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Mostly unknown.&lt;br /&gt;
 bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)&lt;br /&gt;
 bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)&lt;br /&gt;
 bit4 - Enable interlaced signal (use bit3 to swap fields)&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x83&lt;br /&gt;
| Driving curve 1&lt;br /&gt;
| rowspan=3 | &lt;br /&gt;
| rowspan=3 | ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x84-0x97&lt;br /&gt;
| Driving curve 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x98-0xAB&lt;br /&gt;
| Driving curve 3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22381</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22381"/>
		<updated>2023-10-04T22:26:20Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add some more controller 0x01 register info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert (D = ~D, or D = 255 - D)&lt;br /&gt;
 bit1 - color format remap (D7:D2 &amp;lt;-- D5:D0, that is left shift color data by 2)&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| ???&lt;br /&gt;
| 0x0F&lt;br /&gt;
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22380</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22380"/>
		<updated>2023-10-04T18:19:30Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add more confirmed info about controller 0x01&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
&lt;br /&gt;
 bit0 - color value invert&lt;br /&gt;
 bit1 - color format remap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
 bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
&lt;br /&gt;
 bit0 - Horizontal Flip (scan from right to left)&lt;br /&gt;
 bit1 - red-blue swap&lt;br /&gt;
 bit2 - ???&lt;br /&gt;
 bit3 - ???&lt;br /&gt;
 bit4 - ???&lt;br /&gt;
 bit5 - ???&lt;br /&gt;
 bit6 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22379</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22379"/>
		<updated>2023-10-04T18:04:39Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add more freshly discovered I2CLCD registers&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a second set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock a 2nd set of factory controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller.&lt;br /&gt;
&lt;br /&gt;
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don&#039;t write to &amp;quot;standard&amp;quot; locations (other than register 0x40) before locking factory mode back!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| Factory key 2&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock advanced IPS curve controls.&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0xAF:&lt;br /&gt;
* 0x41 - 0x4F&lt;br /&gt;
* 0x58 - 0x5F&lt;br /&gt;
* 0x67 - 0x6F&lt;br /&gt;
* 0xD0 - 0xEF&lt;br /&gt;
* unknown...&lt;br /&gt;
&lt;br /&gt;
Factory mode registers unlocked by register 0x03:&lt;br /&gt;
* 0x04 - 0x0F&lt;br /&gt;
* unknown...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22378</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=22378"/>
		<updated>2023-10-04T16:22:23Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Start adding more info on I2CLCD&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: GPU off&lt;br /&gt;
  bit25: GPU on&lt;br /&gt;
  bit26: bottom backlight off&lt;br /&gt;
  bit27: bottom backlight on&lt;br /&gt;
  bit28: top backlight off&lt;br /&gt;
  bit29: top backlight on&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
These are the chip-on-glass display controllers, also known as I2CLCD.&lt;br /&gt;
&lt;br /&gt;
=== Shared registers ===&lt;br /&gt;
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| Display enable&lt;br /&gt;
| 0x11&lt;br /&gt;
| Values:&lt;br /&gt;
&lt;br /&gt;
  - 0x00 - screen off, slow burn-in&lt;br /&gt;
  - 0x01 - screen off, fast burn-in&lt;br /&gt;
  - 0x10 - screen on, color input used&lt;br /&gt;
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| Read address&lt;br /&gt;
| &lt;br /&gt;
| Write to this register to set the read address.&lt;br /&gt;
&lt;br /&gt;
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Checksum? trigger&lt;br /&gt;
| 0x01&lt;br /&gt;
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it&#039;s oneshot.&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| ???&lt;br /&gt;
| 0x03 (all) /&lt;br /&gt;
0x07 (2DS)&lt;br /&gt;
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).&lt;br /&gt;
&lt;br /&gt;
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| Checksum?&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Read-writable with no effect (old3DS) / read-only (all).&lt;br /&gt;
&lt;br /&gt;
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it&#039;s oneshot/bugged.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown. 0x00 is written here during init. Seems to have no effect.&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| Register checksum&lt;br /&gt;
| &lt;br /&gt;
| Some - but not all - register values are combined using an unknown algorithm into this register.  &lt;br /&gt;
It&#039;s unknown which registers influence this value, as some registers which influence this value are read-only.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| ???&lt;br /&gt;
| 0x01&lt;br /&gt;
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown, does nothing. 0xAA is written here during init.&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF&lt;br /&gt;
| Controller ID&lt;br /&gt;
| &lt;br /&gt;
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.&lt;br /&gt;
&lt;br /&gt;
Known IDs:&lt;br /&gt;
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL&lt;br /&gt;
  - 0xC3 - older old3DSXL&lt;br /&gt;
  - 0xE1 - 2DS&lt;br /&gt;
  - 0x10 - some select new3DS and new3DSXL with IPS screens&lt;br /&gt;
  - 0x01 - old3DS&lt;br /&gt;
  - 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path&lt;br /&gt;
&lt;br /&gt;
Manufacturers:&lt;br /&gt;
  - 0xC - SHARP (TN)&lt;br /&gt;
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles&lt;br /&gt;
  - 0xE - unknown, found in 2DS only&lt;br /&gt;
  - 0x0 - unknown, found in old3DS (non-XL) only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x00 ===&lt;br /&gt;
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x10 to initialize.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| Unknown. Write 0x01 to initialize.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x01 ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Interface config&lt;br /&gt;
| 0xF7&lt;br /&gt;
| Regonfigures the input pins and pin behavior of the controller.&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| Image config&lt;br /&gt;
| 0x7F&lt;br /&gt;
| Image filters and pixel clock control.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| ???&lt;br /&gt;
| 0x11&lt;br /&gt;
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| ???&lt;br /&gt;
| 0x73&lt;br /&gt;
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC3 ===&lt;br /&gt;
Basically the same as Controller ID 0xC7.&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xC7 ===&lt;br /&gt;
This is the most common non-old3DS display controller. Quite overclockable.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0xE1 ===&lt;br /&gt;
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.&lt;br /&gt;
&lt;br /&gt;
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0xAF&lt;br /&gt;
| Factory key&lt;br /&gt;
| &lt;br /&gt;
| Write 0xAA here to unlock factory controls.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Custom registers for controller 0x10 ===&lt;br /&gt;
JDI IPS controller. It&#039;s currently unknown how to unlock factory registers.&lt;br /&gt;
&lt;br /&gt;
Factory mode registers:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Name&lt;br /&gt;
!  Valid bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x70-0x7F&lt;br /&gt;
| Driving curve 1-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x80-0x8F&lt;br /&gt;
| Driving curve 1-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x90-0x9F&lt;br /&gt;
| Driving curve 2-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xA0-0xAF&lt;br /&gt;
| Driving curve 2-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xB0-0xBF&lt;br /&gt;
| Driving curve 3-1&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xC0-0xCF&lt;br /&gt;
| Driving curve 3-2&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=22304</id>
		<title>Hardware calibration</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=22304"/>
		<updated>2023-08-13T17:27:39Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Update ConfigInfoBlk numbers from cfg sysmodule&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=File format=&lt;br /&gt;
The file consists out of a 0x200 big header (though the actual header is only 0x30 bytes, the rest is zerofilled), plus data whose size is indicated in the header.&lt;br /&gt;
&lt;br /&gt;
==Header==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| Magic &amp;quot;CCAL&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x4&lt;br /&gt;
| Data size, always 0x7D0&lt;br /&gt;
|-&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| Model version (?)&lt;br /&gt;
|-&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| CAL revision (incremented each time the CAL file is updated)&lt;br /&gt;
|-&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x2&lt;br /&gt;
| [[#Aging masks|Bitmask of successful Aging tests]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x20&lt;br /&gt;
| Signature of the data section.&lt;br /&gt;
&lt;br /&gt;
HMACSHA256 is used always except in the below cases where SHA256 is used:&lt;br /&gt;
  - devunits&lt;br /&gt;
  - PARTNER-DEBUGGER&lt;br /&gt;
  - PARTNER-CAPTURE&lt;br /&gt;
  - the SNAKE counterparts of the above&lt;br /&gt;
  - SNAKE-IS-DEBUGGER&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x1D0&lt;br /&gt;
| Zerofilled, padding for the 512byte block size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Aging masks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| FCRAM&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| LCD &amp;quot;flicker&amp;quot;/contrast (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Camera&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Touch panel (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Circle pad (analog stick)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Codec&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Gyroscope&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| RTC&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Accelerometer&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Surround&lt;br /&gt;
|-&lt;br /&gt;
| A&lt;br /&gt;
| Adaptive BackLight (ABL)&lt;br /&gt;
|-&lt;br /&gt;
| B&lt;br /&gt;
| 3D screen (ULCD)&lt;br /&gt;
|-&lt;br /&gt;
| C&lt;br /&gt;
| Backlight PWM&lt;br /&gt;
|-&lt;br /&gt;
| D&lt;br /&gt;
| Analog stick A (???)&lt;br /&gt;
|-&lt;br /&gt;
| E&lt;br /&gt;
| Camera extensions&lt;br /&gt;
|-&lt;br /&gt;
| F&lt;br /&gt;
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Data blocks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]&lt;br /&gt;
!  Since version&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x00040000&lt;br /&gt;
| &lt;br /&gt;
| [[#Touch|Touch panel]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| [[#Circle_pad|Circle pad]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 2*&lt;br /&gt;
| 0x00050000&lt;br /&gt;
| &lt;br /&gt;
| [[#Screen_flicker|Display panel contrast]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| 1*&lt;br /&gt;
| 0x00010000&lt;br /&gt;
| &lt;br /&gt;
| [[#RTC|RTC]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 1*&lt;br /&gt;
| 0x00030000&lt;br /&gt;
| &lt;br /&gt;
| DSPRAM related&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x8A&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| [[#Camera_position|Camera position]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| 0x12&lt;br /&gt;
| 0x00040002&lt;br /&gt;
| &lt;br /&gt;
| [[#Gyro|Gyroscope]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xD0&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x00040003&lt;br /&gt;
| &lt;br /&gt;
| [[#Accel|Accelerometer]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xE0&lt;br /&gt;
| 0x134&lt;br /&gt;
| 0x00020000&lt;br /&gt;
| ???&lt;br /&gt;
| [[#CDC|Codec]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x218&lt;br /&gt;
| 0x06&lt;br /&gt;
| 0x00050007&lt;br /&gt;
| 0x10&lt;br /&gt;
| [[#PIT|Programmable Infrared Transmitter (PIT)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x220&lt;br /&gt;
| 0x214&lt;br /&gt;
| 0x00070000&lt;br /&gt;
| 0x7&lt;br /&gt;
| [[#3D_filters|3D filters]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x440&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x00050003&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[#ABL|Adaptive BackLight / Power saving mode]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x470&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x00050005&lt;br /&gt;
| 0xA&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x4A0&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x00050002&lt;br /&gt;
| 0xB&lt;br /&gt;
| [[#BLPWM|Backlight PWM]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E0&lt;br /&gt;
| 0x18&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| [[#Circle_pad_extra|Circle pad extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x500&lt;br /&gt;
| 0xC&lt;br /&gt;
| ???&lt;br /&gt;
| &lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x510&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x00050004&lt;br /&gt;
| 0x8&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x540&lt;br /&gt;
| 0x08&lt;br /&gt;
| 0x00120000&lt;br /&gt;
| 0x7&lt;br /&gt;
| [[#MCU|MCU]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x550&lt;br /&gt;
| 0x02&lt;br /&gt;
| 0x00050006&lt;br /&gt;
| 0x9&lt;br /&gt;
| [[#ULCD_delay|3D screen (ULCD) delay]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x560&lt;br /&gt;
| 0x08&lt;br /&gt;
| 0x00070002&lt;br /&gt;
| 0xD&lt;br /&gt;
| [[#Microphone_echo_cancel|Microphone echo cancellation]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x570&lt;br /&gt;
| 0x10C&lt;br /&gt;
| 0x00050008&lt;br /&gt;
| 0xF&lt;br /&gt;
| [[#ABL_extra|Power saving mode (ABL) extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x680&lt;br /&gt;
| 0x08&lt;br /&gt;
| 0x00040004&lt;br /&gt;
| 0xF&lt;br /&gt;
| [[#CStick|CStick (Right stick)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x690&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x00180001&lt;br /&gt;
| 0x12&lt;br /&gt;
| [[#QTM|Quad Tracking Module (QTM)]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Data block formats=&lt;br /&gt;
&lt;br /&gt;
==Touch==&lt;br /&gt;
Used for mapping touch ADC values to display pixel co-ordinates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
[4096, 4096] --&amp;gt; [320, 240]&lt;br /&gt;
&lt;br /&gt;
[RawX, RawY] --&amp;gt; [PointX, PointY]&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually [PointX0, PointY0] is placed around 25% from the top-left corner, and the same for [PointX1, PointY1] except 25% from the bottom-right corner.&lt;br /&gt;
This offsetting is needed because the touch film starts to distort outside of that rectangle, which would skew the touch results near the center of the screen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 RawX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 RawY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 PointX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 PointY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 RawX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 RawY1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 PointX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PointY1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad==&lt;br /&gt;
&lt;br /&gt;
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 CenterX&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Raw analog values corresponding to zero input position&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 CenterY&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Screen flicker==&lt;br /&gt;
&lt;br /&gt;
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 FlickerTop&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Contrast voltage&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| u8 FlickerBottom&lt;br /&gt;
|-&lt;br /&gt;
| 2*&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Inline checksum&lt;br /&gt;
| Checksum low byte, NOT THIS[0]&lt;br /&gt;
|-&lt;br /&gt;
| 3*&lt;br /&gt;
| Checksum high byte, THIS[1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==RTC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 CompensationValue&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 1*&lt;br /&gt;
| &lt;br /&gt;
| Checksum byte, NOT THIS[0]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Camera position==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 flags&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float scale&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float RotationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| float RotationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| float ViewAngleRight&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| float ViewAngleLeft&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| float ChartDistance(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| float CameraDistance&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s16 ImageWidth&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s16 ImageHeight&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| u8 reserved[0x10]&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| u8 ???[0x40]&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| s16 aeBaseTarget(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x82&lt;br /&gt;
| s16 kRL&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 kGL&lt;br /&gt;
|-&lt;br /&gt;
| 0x86&lt;br /&gt;
| s16 kBL&lt;br /&gt;
|-&lt;br /&gt;
| 0x88&lt;br /&gt;
| s16 ccmPosition&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Gyro==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 ZeroX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 PlusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 MinusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ZeroY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 PlusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 ZeroZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PlusZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 MinusZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Accel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 OffsetX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 OffsetY&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 OffsetZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 ScaleZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CDC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 DriverGainHP&lt;br /&gt;
| Headphone gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 DriverGainSP&lt;br /&gt;
| Speaker gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u8 AnalogVolumeHP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| u8 AnalogVolumeSP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s8 ShutterVolume[2]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| u8 MicrophoneBias&lt;br /&gt;
| Capacitive microphone bias voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x07&lt;br /&gt;
| u8 QuickCharge&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u8 PGA_GAIN&lt;br /&gt;
| ??? (microphone gain)&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| u8 reserved[3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 FilterHP32[3*5]&lt;br /&gt;
| Headphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s16 FilterHP47[3*5]&lt;br /&gt;
| Headphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| s16 FilterSP32[3*5]&lt;br /&gt;
| Speaker filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x66&lt;br /&gt;
| s16 FilterSP47[3*5]&lt;br /&gt;
| Speaker filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 FilterMic32[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| s16 FilterMic47[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xF4&lt;br /&gt;
| s16 FilterFree[(1+2)+((1+4)*5)]&lt;br /&gt;
| Unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x12C&lt;br /&gt;
| u8 AnalogInterval&lt;br /&gt;
|-&lt;br /&gt;
| 0x12D&lt;br /&gt;
| u8 AnalogStabilize&lt;br /&gt;
|-&lt;br /&gt;
| 0x12E&lt;br /&gt;
| u8 AnalogPrecharge&lt;br /&gt;
|-&lt;br /&gt;
| 0x12F&lt;br /&gt;
| u8 AnalogSense&lt;br /&gt;
|-&lt;br /&gt;
| 0x130&lt;br /&gt;
| u8 AnalogDebounce&lt;br /&gt;
|-&lt;br /&gt;
| 0x131&lt;br /&gt;
| u8 Analog_XP_Pullup&lt;br /&gt;
|-&lt;br /&gt;
| 0x132&lt;br /&gt;
| u8 YM_Driver&lt;br /&gt;
| ??? (circle-pad or touch panel related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x133&lt;br /&gt;
| u8 reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==PIT==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 VisibleFactor&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 IRFactor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==3D filters==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 SpecialFilter[0x100]&lt;br /&gt;
|-&lt;br /&gt;
| 0x200&lt;br /&gt;
| u32 IIRSurroundFilter[5]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 DitherPattern&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 StartX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 StartY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u16 SizeX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| u16 SizeY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 GTH_Ratio&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| u8 DitherMode&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| u8 MinRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| u8 MaxRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| u8 MinGTH&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 MinMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x13&lt;br /&gt;
| u8 ExMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| u8 inertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
| u8 LutListRS[9]&lt;br /&gt;
|-&lt;br /&gt;
| 0x1E&lt;br /&gt;
| u8 reserved[2]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==BLPWM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float coefficient[3][3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| u8 NumLevels&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| u8 padding&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| u16 brightnesses[7];&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| u16 BaseDivisor&lt;br /&gt;
|-&lt;br /&gt;
| 0x36&lt;br /&gt;
| u16 MinimumBrightnessHw&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 MaxX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 MaxY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 MinY&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 type&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 unknown_padding[6]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MCU==&lt;br /&gt;
&lt;br /&gt;
Somewhat misleading, these values are actually used for clamping the MCU&#039;s raw slider readings to comprehensible values.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 SVR2_Min&lt;br /&gt;
| Raw 3D volume slider values &amp;lt;= this map to 3D slider value 0.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 SVR2_Max&lt;br /&gt;
| Raw 3D volume slider values &amp;gt;= this map to 3D slider value 1.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 VolumeSliderMin&lt;br /&gt;
| Written to MCU reg 0x58. Volume slider values &amp;lt;= this map to volume value 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 VolumeSliderMax&lt;br /&gt;
| Written to MCU reg 0x59. Volume slider values &amp;gt;= this map to volume value 0x3F&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ULCD delay==&lt;br /&gt;
&lt;br /&gt;
There is a delay between switching the parallax barrier, and adjusting the backlight.&lt;br /&gt;
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.&lt;br /&gt;
&lt;br /&gt;
This is needed only to prevent epillepsy from analog jitter causing unwanted mode switches, and both values are usually always set to 1 or 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 To2D&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 To3D&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Microphone echo cancel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s8 params[8]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 MaxInertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 pad&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 PWM_CNT_EX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| u32 Histogram1&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u32 Histogram2&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| u32 adjust[0x40]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CStick==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 ThinningCountX(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 ThinningCountY(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 reserved[3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==QTM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float DivisorAtZero (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float HorizontalAngle&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float OptimalDistance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Reading=&lt;br /&gt;
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the &amp;lt;code&amp;gt;i2c:EEP&amp;lt;/code&amp;gt; service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.&lt;br /&gt;
Otherwise attempt is made to read &amp;lt;code&amp;gt;CTRNAND:/ro/sys/HWCAL(0|1).dat&amp;lt;/code&amp;gt; instead.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Mysteries&amp;diff=21914</id>
		<title>Mysteries</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Mysteries&amp;diff=21914"/>
		<updated>2022-07-12T20:41:40Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: replace abbreviations with newly acquired information&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The following is a list of mysteries.&lt;br /&gt;
&lt;br /&gt;
== General ==&lt;br /&gt;
* What is the CTR abbreviation?&lt;br /&gt;
: C may stand for Chiheisen (&amp;quot;horizon&amp;quot; in Japanese, the O3DS&#039;s codename being &amp;quot;Project Horizon&amp;quot;).&lt;br /&gt;
:: Not true, Horizon refers to the OS.&lt;br /&gt;
: CTR stands for Citrus.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Why are there two CTRCARD controllers? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; Also [http://problemkaputt.de/twl-core.jpg DSi SoC pinout] shows evidence of dual NTRCARD controllers on the final DSi SoC. (This was a [http://i.imgur.com/0kJlbEw.png planned feature] of the DSi before being axed later in development)&lt;br /&gt;
&lt;br /&gt;
=== Why are there two EMMC controllers? ===&lt;br /&gt;
&#039;&#039;&#039;Theory:&#039;&#039;&#039; At some point during 3DS hardware development there was an idea to split up CTR and TWL nand into two different chips.&lt;br /&gt;
=== Is there a JTAG? ===&lt;br /&gt;
=== Is there more than one revision of the bootrom? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; Bootrom visible portion has been dumped on the entire 3DS Family (3DS, 3DSXL, 2DS, New3DS, New3DSXL, New2DSXL), and even a prototype board from April(?) 2010. All matching exactly.&lt;br /&gt;
&lt;br /&gt;
=== What is the EMMC controller @ 0x10100000 doing? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; There&#039;s dead code in NWM referencing it.&lt;br /&gt;
=== Why did they put NTRCARD accessible from ARM11? ===&lt;br /&gt;
&#039;&#039;&#039;Theory:&#039;&#039;&#039; At some point during 3DS hardware development there was a concept where ARM11 ran a menu with DS(i) icons while ARM9 was in TWL mode.&lt;br /&gt;
&lt;br /&gt;
=== Is there a secret message embedded in the 3DS keyscrambler constant? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; TWL key scrambler constant was &amp;quot;Nintendo Co., Ltd&amp;quot; in Japanese (&amp;quot;任天堂株式会社&amp;quot;), UTF-16LE encoded, with byte order mark.  The 3DS key scrambler constant, by comparison, is random-looking.&lt;br /&gt;
&lt;br /&gt;
=== What is the PDN abbreviation? ===&lt;br /&gt;
: PowerDowN&lt;br /&gt;
&lt;br /&gt;
=== How does Nintendo reflash bricked systems? ===&lt;br /&gt;
Before trying to boot from NAND, the bootrom checks to see if a key combination (Start + Select + X) is being held, and whether the shell is closed. If so, it tries to boot from an inserted NTR (Nintendo DS) cartridge.&lt;br /&gt;
This allows to execute a FIRM that is probably used by Nintendo to reflash the system.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== What was the problem in &amp;quot;initial program loader&amp;quot; that was mentioned in an FCC filing by Nintendo for 2DS? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; http://www.neogaf.com/forum/showthread.php?t=814624&amp;amp;page=1&lt;br /&gt;
=== What did SVC 0x74 in the ARM11 kernel do before it got stubbed? ===&lt;br /&gt;
=== What is the PTM abbreviation? ===&lt;br /&gt;
: PlayTime Management&lt;br /&gt;
&lt;br /&gt;
=== Why is the DTCM not used anywhere except bootrom? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; Bootrom is known to use part of DTCM as state, memsetting it to 0 when it&#039;s done. After that, it is never used again.&lt;br /&gt;
=== How is CTRAging launched during factory setup? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; No TestMenu version is capable of launching CTRAging directly: O3DS factory TestMenu can only launch DevMenu installed on NAND, the inserted cartridge and the TWL/AGB test apps; N3DS factory TestMenu can only launch DevMenu installed on NAND, the inserted cartridge and System Settings. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Theory:&#039;&#039;&#039; NtrBoot another time &lt;br /&gt;
=== Why are there 4 stubbed syscalls named SendSyncRequest1-4? ===&lt;br /&gt;
=== Is there a deterministic formula for calculating the Movable.sed KeyY high u64? ===&lt;br /&gt;
&#039;&#039;&#039;Background:&#039;&#039;&#039; We know now that the high 4 bytes of KeyY can be reliably estimated to be 1/5th of the LocalFriendCodeSeed (low 8 bytes of KeyY), which is close enough to brute force. However, the actual value is usually about 0-4000 units off the actual high u32 of the KeyY (called msed3 in the seedminer implementation). Could there possibly be a deterministic formula given this 1/5 ratio is so close to the correct value? It&#039;s difficult to imagine this is just a coincidence.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21818</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21818"/>
		<updated>2022-02-12T11:30:01Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Update horizontal timing parameters (continuation)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)&lt;br /&gt;
 - Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HStart&lt;br /&gt;
| Determines when the image is going to be displayed in the visible region (register 0x60).&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HBR&lt;br /&gt;
| Right border start(?). Does nothing.&lt;br /&gt;
&lt;br /&gt;
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HPF&lt;br /&gt;
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.&lt;br /&gt;
&lt;br /&gt;
Unknown why, but a single dot of red is displayed before entering this mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| See [[#Framebuffer_format|framebuffer format]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| [[#Framebuffer_color_formats|Color format]]&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| DMA size&lt;br /&gt;
&lt;br /&gt;
 0 - 64 bytes&lt;br /&gt;
 1 - 128 bytes&lt;br /&gt;
 2 - 256 bytes&lt;br /&gt;
 3 - ???&lt;br /&gt;
&lt;br /&gt;
FCRAM doesn&#039;t support DMA size 3.&lt;br /&gt;
|-&lt;br /&gt;
| 31-16&lt;br /&gt;
| Unknown&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21817</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21817"/>
		<updated>2022-02-11T21:45:00Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Clarify border register&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through xTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in no particular order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Front porch (68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
 - Unknown synchronization/blanking (unknown where it happens)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HParam5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HParam7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HParam8&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold. Enables pushing pixels to the display.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| Bit 0-2: framebuffer format&lt;br /&gt;
Bit 3: null (unwritable)&lt;br /&gt;
Bit 4-7: unknown&lt;br /&gt;
Bit 8-9: DMA size&lt;br /&gt;
Bit 10-15: null (unwritable)&lt;br /&gt;
Bit 16-31: unknown&lt;br /&gt;
&lt;br /&gt;
DMA sizes (in bytes):&lt;br /&gt;
0 - 64&lt;br /&gt;
1 - 128&lt;br /&gt;
2 - 256&lt;br /&gt;
3 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21816</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21816"/>
		<updated>2022-02-11T21:40:01Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add hexadecimal prefix, and clarify relation meaning&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows (values may overflow through xTotal register value):&lt;br /&gt;
 0x10 &amp;lt; 0x14 &amp;lt;= 0x60.LO &amp;lt;= 0x04 &amp;lt;= 0x60.HI &amp;lt;= 0x08 &amp;lt;= 0x0C &amp;lt;= 0x10&lt;br /&gt;
 0x18 &amp;lt;= 0x60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in no particular order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Front porch (68 clock min, otherwise the screen doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
 - Unknown synchronization/blanking (unknown where it happens)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HParam5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HParam7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HParam8&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| Bit 0-2: framebuffer format&lt;br /&gt;
Bit 3: null (unwritable)&lt;br /&gt;
Bit 4-7: unknown&lt;br /&gt;
Bit 8-9: DMA size&lt;br /&gt;
Bit 10-15: null (unwritable)&lt;br /&gt;
Bit 16-31: unknown&lt;br /&gt;
&lt;br /&gt;
DMA sizes (in bytes):&lt;br /&gt;
0 - 64&lt;br /&gt;
1 - 128&lt;br /&gt;
2 - 256&lt;br /&gt;
3 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21815</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21815"/>
		<updated>2022-02-11T21:33:01Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Adding new PDC reg names and descriptions (continuation)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows:&lt;br /&gt;
 10 &amp;lt; 14 &amp;lt;= 60.LO &amp;lt;= 04 &amp;lt;= 60.HI &amp;lt;= 08 &amp;lt;= 0C&lt;br /&gt;
 18 &amp;lt;= 60.LO&lt;br /&gt;
&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in no particular order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Front porch (68 clock min, otherwise the screenn doesn&#039;t sync properly, and really glitches out)&lt;br /&gt;
 - Unknown synchronization/blanking (unknown where it happens)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HParam5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HParam7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HParam8&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HSync&lt;br /&gt;
| Triggers a HSync pulse.&lt;br /&gt;
&lt;br /&gt;
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HPB&lt;br /&gt;
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.&lt;br /&gt;
&lt;br /&gt;
The display is blank, and the LCD displays nothing in this period (doesn&#039;t push pixels).&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HBL&lt;br /&gt;
| Left border trigger treshold.&lt;br /&gt;
&lt;br /&gt;
If this value is smaller than the back porch, then the back porch period will be zero, and the border will be immediately displayed upon entering the back porch period.&lt;br /&gt;
&lt;br /&gt;
Can be lower than HSync, as the back porch is what takes the controller out of HSync.&lt;br /&gt;
&lt;br /&gt;
Must be &amp;lt;= HDisp start (reg 0x60 low u16).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| HDisp&lt;br /&gt;
| low u16: Image start (border --&amp;gt; pixel data)&lt;br /&gt;
high u16: Image end (pixel data --&amp;gt; border)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| Bit 0-2: framebuffer format&lt;br /&gt;
Bit 3: null (unwritable)&lt;br /&gt;
Bit 4-7: unknown&lt;br /&gt;
Bit 8-9: DMA size&lt;br /&gt;
Bit 10-15: null (unwritable)&lt;br /&gt;
Bit 16-31: unknown&lt;br /&gt;
&lt;br /&gt;
DMA sizes (in bytes):&lt;br /&gt;
0 - 64&lt;br /&gt;
1 - 128&lt;br /&gt;
2 - 256&lt;br /&gt;
3 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21814</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21814"/>
		<updated>2022-02-11T20:26:13Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Get rid of misleading and wrong information, and add more researched PDC info (unfinished)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size. timin&lt;br /&gt;
&lt;br /&gt;
The horizontal timing parameter order is as follows:&lt;br /&gt;
 0x10 --&amp;gt; 0x14 --&amp;gt; 0x18 --&amp;gt; 0x60 low --&amp;gt; 0x04 --&amp;gt; 0x60 high --&amp;gt; 0x08 --&amp;gt; 0x0C.&lt;br /&gt;
Timing starts from HCount == 0, then each absolute value in the beforementioned register chain triggers when HCount == register, latching the primitive display controller into a new mode.&lt;br /&gt;
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.&lt;br /&gt;
&lt;br /&gt;
 Known latched modes (in no particular order):&lt;br /&gt;
 - HSync (triggers a line to the LCD to move to the next line)&lt;br /&gt;
 - Back porch (area between HSync and border being displayed)&lt;br /&gt;
 - Left border start (no image data is being displayed, just a configurable solid color)&lt;br /&gt;
 - Image start (pixel data is being DMA&#039;d from video memory or main RAM)&lt;br /&gt;
 - Right border start/Image end (border color is being displayed after the main image)&lt;br /&gt;
 - Front porch (unknown where it happens)&lt;br /&gt;
 - Unknown synchronization/blanking (unknown where it happens)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HParam5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| HParam7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| HParam8&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| HParam1&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| HParam2&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| HParam3&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| low u16: ???&lt;br /&gt;
high u16: ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: Image width (including some offset?)&lt;br /&gt;
high u16: Image height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: HParam4&lt;br /&gt;
high u16: HParam6&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format and other settings&lt;br /&gt;
| Bit 0-2: framebuffer format&lt;br /&gt;
Bit 3: null (unwritable)&lt;br /&gt;
Bit 4-7: unknown&lt;br /&gt;
Bit 8-9: DMA size&lt;br /&gt;
Bit 10-15: null (unwritable)&lt;br /&gt;
Bit 16-31: unknown&lt;br /&gt;
&lt;br /&gt;
DMA sizes (in bytes):&lt;br /&gt;
0 - 64&lt;br /&gt;
1 - 128&lt;br /&gt;
2 - 256&lt;br /&gt;
3 - ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21624</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21624"/>
		<updated>2021-11-27T19:36:15Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Fix a missing brace in calculation and make register more clear&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (HTotal + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = PClock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21623</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21623"/>
		<updated>2021-11-27T19:31:38Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Start cleaning up rotated screen clutter from PDC reg descriptions and add some mroe info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.&lt;br /&gt;
&lt;br /&gt;
To make sense of these values, the 3DS must be held in a way, so that the bottom screen is in the left hand, and the top screen is in the right hand, and that way the first pixel will be in the top-left corner, as it should be. If the 3DS is held normally, the first pixel is in the bottom-left corner.&lt;br /&gt;
&lt;br /&gt;
All pixel and scanline timing values are 12bits, unless noted. This also applies to those fields where two u16 are combined into one register. Each u16 field is only 12bits in size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| HTotal&lt;br /&gt;
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.&lt;br /&gt;
&lt;br /&gt;
HClock = PClock / (this value + 1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total height of the timing window. Can be interpreted as the vertical clock divider.&lt;br /&gt;
&lt;br /&gt;
VClock = Pclock / (HTotal + 1) / (VTotal + 1)&lt;br /&gt;
&lt;br /&gt;
Setting this to 494 lowers framerate to about 50.040660858 Hz (268111856 / 24 / (250 + 1) / (494 + 1)).&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| ?&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| ?&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| 24bits(? top 8bits ignored)&lt;br /&gt;
&lt;br /&gt;
When the visible region is being drawn, but the timing parameters are set up in a way that the framebuffer is smaller than the visible region, it will be filled by this color.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| 32bits (bottom 3bits ignored?)&lt;br /&gt;
&lt;br /&gt;
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image, although that requires the framebuffer pointer register to be set to the start of the last scanline, instead of at the start of the framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21622</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21622"/>
		<updated>2021-11-25T20:13:30Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add and update some newly researched PDC regs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| H-total (V-total on not physically rotated screens).&lt;br /&gt;
| 12bits.&lt;br /&gt;
&lt;br /&gt;
Setting this value too low will make the screen not be able to sync any pixels other than a single one from the wrong location. The lowest the screen can handle is 0x1C2, at 0x1C1 the display loses a few scanlines worth of pixel clock (though not noticable).&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HBlank timer(?)&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| H Interrupt timing&lt;br /&gt;
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.&lt;br /&gt;
&lt;br /&gt;
There seems to be some limitations though:&lt;br /&gt;
* low u16 must be smaller than high u16&lt;br /&gt;
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal&lt;br /&gt;
* setting low u16 to &amp;gt;= HTotal disables the interrupt ever firing&lt;br /&gt;
&lt;br /&gt;
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| V-total (H-total on not physically rotated screens).&lt;br /&gt;
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| VBlank timer(?)&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| V Interrupt timing&lt;br /&gt;
| Similar to H Interrupt timing (0x1C), except the comparison is done against VCount, the limitations are emposed on VTotal, and the interrupt that fires is VSync.&lt;br /&gt;
&lt;br /&gt;
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn&#039;t seem to be a register to control this behavior.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| HCount&lt;br /&gt;
| Horizontal &amp;quot;beam position&amp;quot; counter. Note that this value does not equal to the current pixel being drawn.&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| VCount&lt;br /&gt;
| Vertical &amp;quot;beam position&amp;quot; counter. Note that the scanline being drawn isn&#039;t equal to this value.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: HBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
&lt;br /&gt;
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.&lt;br /&gt;
&lt;br /&gt;
Setting this to zero will cause only the first line of the image to be displayed repeated on the entire display. With the HSync interrupt it&#039;s possible to &amp;quot;race the beam&amp;quot; to (ab)use this feature.&lt;br /&gt;
&lt;br /&gt;
Because of this simplicity, writing a negative value here VFlips the image (which appears as a HFlip if the 3DS is held properly)&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=ARM11_Interrupts&amp;diff=21621</id>
		<title>ARM11 Interrupts</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=ARM11_Interrupts&amp;diff=21621"/>
		<updated>2021-11-25T18:39:25Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Clarify PDC interrupt use&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Interrupts ==&lt;br /&gt;
&lt;br /&gt;
Interrupt priority is 0-0xF. A priority of 0xF means that the interrupt is disabled.&lt;br /&gt;
&lt;br /&gt;
= Private Interrupts =&lt;br /&gt;
&lt;br /&gt;
Each CPU core has 32 software interrupts that are private and belong to that core.  These interrupts are numbers 0-0x1F for each core.  The hardware interrupts are not core-specific and start at interrupt ID 0x20.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  IRQ&lt;br /&gt;
!  Listener&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| &lt;br /&gt;
| MPCore software-interrupt. Used by Kernel11 to sync cores in crt0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1-0x3&lt;br /&gt;
| &lt;br /&gt;
| MPCore software-interrupt. Used by Boot11 and Kernel11 to kickstart Core1/2/3, and by Kernel11 to sync cores in crt0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. Used to manage the performance counter. Also used by Kernel11 during crt0 to sync up.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. Does apparently nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. Extensively used by [[SVC|KernelSetState]] (and contains most of the actual code of the latter).&lt;br /&gt;
|-&lt;br /&gt;
| 0x7&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. See [[KCacheMaintenanceInterruptEvent]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. Used for scheduling.&lt;br /&gt;
|-&lt;br /&gt;
| 0x9&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore software-interrupt. Used when handling exceptions that require termination of a thread or a process, and in some cases by svcSetDebugThreadContext, to store VFP registers in the thread&#039;s register storage.&lt;br /&gt;
|-&lt;br /&gt;
| 0xA&lt;br /&gt;
| Kernel&lt;br /&gt;
| TLB operations interrupt, see [[KTLBOperationsInterruptEvent]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xB-0xE&lt;br /&gt;
|&lt;br /&gt;
| MPCore software-interrupt. Not configured.&lt;br /&gt;
|-&lt;br /&gt;
| 0xF&lt;br /&gt;
| dmnt/debugger&lt;br /&gt;
| MPCore software-interrupt. Used to abstract FIQ (debug). This interrupt is never sent to core2 nor core3 on N3DS.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1D&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore timer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1E&lt;br /&gt;
| Kernel&lt;br /&gt;
| MPCore watchdog - set when the watchdog counter reaches 0 in timer mode, causes interrupt 30 to set as pending.  Only set on core 1 as core 1&#039;s timer is used for everything.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Hardware Interrupts =&lt;br /&gt;
&lt;br /&gt;
There are 0x60 hardware interrupts starting at 0x20 and continuing up to 0x7F.  These are not private and are accessible from any core.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  IRQ&lt;br /&gt;
!  Listener&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| ?&lt;br /&gt;
| SPI bus 2 interrupt status update&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| PSC0&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| PSC1&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| PDC0 (Top screen VBlank0, HBlank0)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| PDC1 (Bottom screen VBlank1, HBlank1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| PPF&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| gsp, TwlBg&lt;br /&gt;
| P3D&lt;br /&gt;
|-&lt;br /&gt;
| 0x30-0x38&lt;br /&gt;
| Kernel&lt;br /&gt;
| Old CDMA Event 0..8 (9 separate IRQ lines)&lt;br /&gt;
|-&lt;br /&gt;
| 0x39&lt;br /&gt;
| Kernel&lt;br /&gt;
| Old CDMA Faulting (eg. CCR=0, or event&amp;gt;15)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3A&lt;br /&gt;
| Kernel&lt;br /&gt;
| New CDMA Event 0..31 (shared IRQ line)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| Kernel&lt;br /&gt;
| New CDMA Faulting (eg. CCR=0)&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| nwm&lt;br /&gt;
| WIFI SDIO Controller @ 0x10122000&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| nwm&lt;br /&gt;
| WIFI SDIO Controller IRQ pin @ 0x10122000&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| nwm_dev?&lt;br /&gt;
| Debug WIFI SDIO Controller @ 0x10100000 ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| nwm_dev?&lt;br /&gt;
| Debug WIFI SDIO Controller @ 0x10100000 ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ?&lt;br /&gt;
| NTRCARD (maybe?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
| mvd (New3DS)&lt;br /&gt;
| L2B_0 (First RGB-to-RGBA Converter)&lt;br /&gt;
|-&lt;br /&gt;
| 0x46&lt;br /&gt;
| mvd (New3DS)&lt;br /&gt;
| L2B_1 (Second RGB-to-RGBA Converter)&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| camera&lt;br /&gt;
| Camera Bus 0 (DSi cameras)&lt;br /&gt;
|-&lt;br /&gt;
| 0x49&lt;br /&gt;
| camera&lt;br /&gt;
| Camera Bus 1 (left-eye)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4A&lt;br /&gt;
| dsp&lt;br /&gt;
| General interrupt from DSP, including semaphore and command/reply registers status change&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| camera&lt;br /&gt;
| Y2R Conversion Finished&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| TwlBg&lt;br /&gt;
| LGYFB_0 Legacy GBA/NDS Video&lt;br /&gt;
|-&lt;br /&gt;
| 0x4D&lt;br /&gt;
| TwlBg&lt;br /&gt;
| LGYFB_1 Legacy GBA/NDS Video&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| mvd (New3DS)&lt;br /&gt;
| Y2R2 End Event&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| mvd (New3DS)&lt;br /&gt;
| MVD general interrupt?&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| pxi, TwlBg&lt;br /&gt;
| Sync (bit 29 from Arm9&#039;s PXI_SYNC)&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| pxi, TwlBg&lt;br /&gt;
| Sync 2 (bit 30 from Arm9&#039;s PXI_SYNC)&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
| pxi, TwlBg&lt;br /&gt;
| Send Fifo Empty&lt;br /&gt;
|-&lt;br /&gt;
| 0x53&lt;br /&gt;
| pxi, TwlBg&lt;br /&gt;
| Receive Fifo Not Empty&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| i2c, TwlBg&lt;br /&gt;
| I2C Bus0 work done&lt;br /&gt;
|-&lt;br /&gt;
| 0x55&lt;br /&gt;
| i2c, TwlBg&lt;br /&gt;
| I2C Bus1 work done&lt;br /&gt;
|-&lt;br /&gt;
| 0x56&lt;br /&gt;
| spi, TwlBg&lt;br /&gt;
| SPI bus 3 interrupt status update&lt;br /&gt;
|-&lt;br /&gt;
| 0x57&lt;br /&gt;
| spi, TwlBg&lt;br /&gt;
| SPI bus 1 interrupt status update&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| Kernel&lt;br /&gt;
| PDN (wake event or SoC mode changed)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| TwlBg&lt;br /&gt;
| PDN Legacy Sleep&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| mic&lt;br /&gt;
| General microphone interrupt (?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
| -&lt;br /&gt;
| [[HID_Registers#HID_PAD_CNT|HID_PAD_CNT]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| i2c, TwlBg&lt;br /&gt;
| I2C Bus2 work done&lt;br /&gt;
|-&lt;br /&gt;
| 0x5F&lt;br /&gt;
| mp&lt;br /&gt;
| DS WiFi registers&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Shell opened (GPIO1_2 falling edge)&lt;br /&gt;
|-&lt;br /&gt;
| 0x62&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Shell closed (GPIO1_2 rising edge)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Touch Screen pressed (GPIO1_1 falling edge)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Headphones inserted (GPIO2_0)&lt;br /&gt;
|-&lt;br /&gt;
| 0x66&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| TWL depop circuit (GPIO2_1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| C-stick interrupt (GPIO3_0)&lt;br /&gt;
|-&lt;br /&gt;
| 0x69&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| IrDA interrupt (active-low) (GPIO3_1)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6A&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Gyro interrupt (GPIO3_2)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6B&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| C-stick &amp;quot;stop&amp;quot; (output) (GPIO3_3)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| IrDA TX-RC (output) (GPIO3_4)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6D&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| IrDA RXD (GPIO3_5)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6E&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| NFC output1 (?) (GPIO3_6)&lt;br /&gt;
|-&lt;br /&gt;
| 0x6F&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| NFC output2 (?) (GPIO3_7)&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| Headphones button/half-inserted (active-low) (GPIO3_8)&lt;br /&gt;
|-&lt;br /&gt;
| 0x71&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| MCU interrupt (GPIO3_9)&lt;br /&gt;
|-&lt;br /&gt;
| 0x72&lt;br /&gt;
| gpio, TwlBg&lt;br /&gt;
| NFC interrupt (?) (GPIO3_10)&lt;br /&gt;
|-&lt;br /&gt;
| 0x73&lt;br /&gt;
| TwlBg&lt;br /&gt;
| QTM output (?) (GPIO3_11)&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| ?&lt;br /&gt;
| Gamecard related&lt;br /&gt;
|-&lt;br /&gt;
| 0x75&lt;br /&gt;
| ?&lt;br /&gt;
| Gamecard inserted&lt;br /&gt;
|-&lt;br /&gt;
| 0x76&lt;br /&gt;
| -&lt;br /&gt;
| L2C&lt;br /&gt;
|-&lt;br /&gt;
| 0x78 to 0x7B&lt;br /&gt;
| Kernel&lt;br /&gt;
| Core 0-3 Performance monitor counter (any) overflow&lt;br /&gt;
|-&lt;br /&gt;
| 0x7A to 0x82 (PDN_MPCORE_CFG bit2 set) or&lt;br /&gt;
0x7C to 0x84 (bit2 clear)&lt;br /&gt;
| Kernel&lt;br /&gt;
| Other PMU interrupts (line may not exist at all)&lt;br /&gt;
|}&lt;br /&gt;
(interrupts from 0x80 and up can&#039;t be mapped in available builds of the kernel)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There are 2 tables in the Arm11 kernel: the first has 32 * 2(or 32 * 4) 8-byte entries. This table is for the private interrupts that belong to each core.  The data for each interrupt can be found by doing table_base + (core_num * 0x100) + (intr_num * 8).  The second table is for public hardware interrupts and the data for each interrupt can be retrieved by doing table_base + (intr_num * 8).&lt;br /&gt;
&lt;br /&gt;
The Arm11 kernel configures interrupts the following way (it seems the GPIO IRQ layout doesn&#039;t match released 3DS models):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Interrupts 0x00 to 0x1F: edge-triggered, N-N&lt;br /&gt;
Interrupt 0x20: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x21: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x22: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x23: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x24: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x25: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x28: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x29: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x2a: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x2b: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x2c: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x2d: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x30: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x31: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x32: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x33: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x34: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x35: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x36: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x37: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x38: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x39: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x3a: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x3b: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x40: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x41: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x42: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x43: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x44: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x45: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x46: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x48: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x49: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4a: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4b: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4c: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4d: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4e: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x4f: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x50: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x51: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x52: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x53: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x54: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x55: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x56: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x57: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x58: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x59: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x5a: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x5b: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x5f: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x60: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x61: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x64: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x65: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x66: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x68: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x69: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6a: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6b: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6c: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6d: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6e: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x6f: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x70: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x71: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x72: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x73: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x74: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x75: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x76: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x77: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x78: edge-triggered, 1-N&lt;br /&gt;
Interrupt 0x79: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x7a: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x7b: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x7c: level-sensitive, 1-N&lt;br /&gt;
Interrupt 0x7d: level-sensitive, 1-N&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= InterruptData =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Type&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| [[KBaseInterruptEvent]] *&lt;br /&gt;
| Pointer to the KBaseInterruptEvent object for this interrupt &lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| u8&lt;br /&gt;
| Interrupt will be disabled by the IRQ handler as soon as it is acknowledged.&lt;br /&gt;
Ignored for FIQ: the FIQ handler always sets bit2 of [[PDN_Registers#PDN_FIQ_CNT|PDN_FIQ_CNT]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x5&lt;br /&gt;
| u8&lt;br /&gt;
| Interrupt is disabled&lt;br /&gt;
|-&lt;br /&gt;
| 0x6&lt;br /&gt;
| u8&lt;br /&gt;
| Interrupt priority&lt;br /&gt;
|-&lt;br /&gt;
| 0x7&lt;br /&gt;
| u8&lt;br /&gt;
| Unused, alignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Interrupt Table (New3DS) =&lt;br /&gt;
(0xFFF318F4 in 10.3)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Type&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| InterruptData[224]&lt;br /&gt;
| Data for all hardware and software interrupts&lt;br /&gt;
|-&lt;br /&gt;
| 0x700&lt;br /&gt;
| [[KObjectMutex]]&lt;br /&gt;
| Mutex&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Pinouts&amp;diff=21593</id>
		<title>Pinouts</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Pinouts&amp;diff=21593"/>
		<updated>2021-09-24T03:35:22Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: [WIP] Update MCU info + reformat table + merge TP numbers from page history&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CTR CPU B ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;font-family:Monospace;text-align:center;width:100%;table-layout:fixed;&amp;quot;&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #cc9900&amp;quot; | 0? || style=&amp;quot;background: #336600&amp;quot; | CS1 || style=&amp;quot;background: #336600&amp;quot; | ? || style=&amp;quot;background: #336600&amp;quot; | ? || style=&amp;quot;background: #a060a0&amp;quot; | D5  || style=&amp;quot;background: #a060a0&amp;quot; | D2  ||     || style=&amp;quot;background: #a060a0&amp;quot; | RST || style=&amp;quot;background: #a060a0&amp;quot; | CLK || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff0000&amp;quot; | X  || style=&amp;quot;background: #ff0000&amp;quot; | X  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     ||     ||     ||     || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? ||     || style=&amp;quot;background: #666633&amp;quot; | IRIRQ || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #bbbbbb&amp;quot; |  G&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc9900&amp;quot; | 1? || style=&amp;quot;background: #cc9900&amp;quot; | 2? || style=&amp;quot;background: #336600&amp;quot; | CSx || style=&amp;quot;background: #336600&amp;quot; | CSy || style=&amp;quot;background: #336600&amp;quot; | ? || style=&amp;quot;background: #a060a0&amp;quot; | D6  || style=&amp;quot;background: #a060a0&amp;quot; | D3  || style=&amp;quot;background: #a060a0&amp;quot; | D0  || style=&amp;quot;background: #a060a0&amp;quot; | IRQ || style=&amp;quot;background: #a060a0&amp;quot; | CS1 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||     ||     || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? ||     ||     ||     || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc9900&amp;quot; | 3? ||     ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| || ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     || style=&amp;quot;background: #a060a0&amp;quot; | D7  || style=&amp;quot;background: #a060a0&amp;quot; | D4  || style=&amp;quot;background: #a060a0&amp;quot; | D1  || style=&amp;quot;background: #a060a0&amp;quot; | DET || style=&amp;quot;background: #a060a0&amp;quot; | CS2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #a52a2a&amp;quot; | ? || style=&amp;quot;background: #666633&amp;quot; | IRTX || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffff00&amp;quot; | CLK || style=&amp;quot;background: #ffff00&amp;quot; | D0  ||style=&amp;quot;background: #ffffff&amp;quot; | || || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffff00&amp;quot; | D1  || style=&amp;quot;background: #ffff00&amp;quot; | D2  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ffff00&amp;quot; | D3  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffff00&amp;quot; | CMD || style=&amp;quot;background: #ffff00&amp;quot; | IRQ ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ffff00&amp;quot; | WP  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #00aaee&amp;quot; | CLK || style=&amp;quot;background: #00aaee&amp;quot; | D0  ||style=&amp;quot;background: #ffffff&amp;quot; | || || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #00aaee&amp;quot; | D1  || style=&amp;quot;background: #00aaee&amp;quot; | D2  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #00aaee&amp;quot; | D3  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
|     || style=&amp;quot;background: #00aaee&amp;quot; | CMD ||style=&amp;quot;background: #ffffff&amp;quot; | ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #20b2aa&amp;quot; | ? || style=&amp;quot;background: #20b2aa&amp;quot; | ? ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #20b2aa&amp;quot; | ? || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
|     || style=&amp;quot;background: #20b2aa&amp;quot; | ? ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #20b2aa&amp;quot; | ? || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
|     ||     ||style=&amp;quot;background: #ffffff&amp;quot; | ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
|     ||     ||style=&amp;quot;background: #ffffff&amp;quot; | ||     || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #33ffff&amp;quot; | SCL ||     ||style=&amp;quot;background: #ffffff&amp;quot; | ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #33ffff&amp;quot; | SDA  ||     ||style=&amp;quot;background: #ffffff&amp;quot; | ||     || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
|     || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v8 || style=&amp;quot;background: #d9ffb3&amp;quot; | 1v2 || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #cc6600&amp;quot; | ?  || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #73e600&amp;quot; | ?  || style=&amp;quot;background: #476b6b&amp;quot; | 3? || style=&amp;quot;background: #476b6b&amp;quot; | 4? || style=&amp;quot;background: #476b6b&amp;quot; | 5? ||    ||     ||     ||     || style=&amp;quot;background: #ff69b4&amp;quot; |  B  || style=&amp;quot;background: #ff69b4&amp;quot; | PADR || style=&amp;quot;background: #ff69b4&amp;quot; | PADD || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | ?  || style=&amp;quot;background: #cc6600&amp;quot; | ?  ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | ||style=&amp;quot;background: #ffffff&amp;quot; | || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? ||     || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #73e600&amp;quot; | ?  || style=&amp;quot;background: #8efab4&amp;quot; | SDA || style=&amp;quot;background: #476b6b&amp;quot; | 1? || style=&amp;quot;background: #476b6b&amp;quot; | 2? ||     ||     ||     ||     || style=&amp;quot;background: #ff69b4&amp;quot; |  A  || style=&amp;quot;background: #ff69b4&amp;quot; | STRT || style=&amp;quot;background: #ff69b4&amp;quot; | PADU || style=&amp;quot;background: #ff69b4&amp;quot; |  L  || style=&amp;quot;background: #ff69b4&amp;quot; |  Y  || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; |  G  || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? ||     || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? ||     || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ? || style=&amp;quot;background: #b19cd9&amp;quot; | ?  || style=&amp;quot;background: #8efab4&amp;quot; | SCL || style=&amp;quot;background: #476b6b&amp;quot; | 0? ||     ||     ||     ||     ||     ||     || style=&amp;quot;background: #ff69b4&amp;quot; | SLCT || style=&amp;quot;background: #ff69b4&amp;quot; | PADL ||  style=&amp;quot;background: #ff69b4&amp;quot; |  R  || style=&amp;quot;background: #ff69b4&amp;quot; |  X  || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ? || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  || style=&amp;quot;background: #ff2a7f&amp;quot; | ?  ||  style=&amp;quot;background: #bbbbbb&amp;quot; | G&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
legend:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;font-family:Monospace;text-align:center;table-layout:fixed;&amp;quot;&lt;br /&gt;
| style=&amp;quot;background: #ff0000&amp;quot; | SoC clock crystal&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffaaaa&amp;quot; | RTC clock crystal&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #a060a0&amp;quot; | Gamecard&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffff00&amp;quot; | SDCARD SDIO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #00aaee&amp;quot; | NAND SDIO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #20b2aa&amp;quot; | WIFI SDIO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #336600&amp;quot; | SPI&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #73e600&amp;quot; | I2C-1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #8efab4&amp;quot; | I2C-2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #33ffff&amp;quot; | I2C-3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff69b4&amp;quot; | Pad&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff2a7f&amp;quot; | FCRAM&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #b19cd9&amp;quot; | Camera&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #a52a2a&amp;quot; | WIFI&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #666633&amp;quot; | GPIO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ff8000&amp;quot; | LCD0 (small)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc6600&amp;quot; | LCD1 (big)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #cc9900&amp;quot; | CODEC0 (unknown)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #476b6b&amp;quot; | CODEC1 (unknown)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #4d4d33&amp;quot; | MCU (unknown)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #d9ffb3&amp;quot; | POWER&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; | Ground&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Orientation: Triangle bottom right on the PCB.&lt;br /&gt;
&lt;br /&gt;
== UC CTR ==&lt;br /&gt;
&lt;br /&gt;
The MCU seems to most closely resemble an NEC (Renesas) 78K0R/Kx3-L 64-pin FBGA: https://www.renesas.com/us/en/document/mah/78k0rkx3-l-users-manual-hardware-r01uh0106ej040078k0rkx3l?language=en&amp;amp;r=1051991&lt;br /&gt;
&lt;br /&gt;
The functional pin mapping is almost exactly the same, except the GPIO port assignment is almost completely different.&lt;br /&gt;
&lt;br /&gt;
Most low port numbers appear to map to the correct physical pin locations as described in the above datasheet, however around P7 and above this mapping is definitely altered.&lt;br /&gt;
&lt;br /&gt;
Orientation: Pin 1 marker in bottom left corner&lt;br /&gt;
&lt;br /&gt;
===Pinout===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;font-family:Monospace;text-align:center;width:100%;table-layout:fixed;width:26%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;background: #d9ffb3&amp;quot; | + || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || || TP75 || style=&amp;quot;background: #ffaaaa&amp;quot; | X || style=&amp;quot;background: #ffaaaa&amp;quot; | X || style=&amp;quot;background: #4d4d33&amp;quot; | ? &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 7&lt;br /&gt;
| style=&amp;quot;background: #73e600&amp;quot; | SCL || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || /RESET || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #4d4d33&amp;quot; | ? || style=&amp;quot;background: #d9ffb3&amp;quot; | + &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 6&lt;br /&gt;
| style=&amp;quot;background: #73e600&amp;quot; | SDA || || style=&amp;quot;background: #d9ffb3&amp;quot; | + || TP77 || TP76 || || || style=&amp;quot;background: #d9ffb3&amp;quot; | + &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 5&lt;br /&gt;
| style=&amp;quot;background: #4d4d33&amp;quot; | ? || || TP78 || PWRLED1 || || || || CHRGLED&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 4&lt;br /&gt;
| || || || || || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 3&lt;br /&gt;
| || PWRBTN || || || || || BATTTHM ||&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 2&lt;br /&gt;
| || || || PWRLED0 || || || HOMEBTN ||&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background: #eaecf0&amp;quot; | 1&lt;br /&gt;
| style=&amp;quot;background: #d9ffb3&amp;quot; | + || || || || style=&amp;quot;background: #8efab4&amp;quot; | SCL || style=&amp;quot;background: #8efab4&amp;quot; | SDA || || style=&amp;quot;background: #bbbbbb&amp;quot; | G &lt;br /&gt;
|-&lt;br /&gt;
!/&lt;br /&gt;
!A&lt;br /&gt;
!B&lt;br /&gt;
!C&lt;br /&gt;
!D&lt;br /&gt;
!E&lt;br /&gt;
!F&lt;br /&gt;
!G&lt;br /&gt;
!H&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Pin assignment===&lt;br /&gt;
&lt;br /&gt;
 und = undocumented / custom&lt;br /&gt;
 SFR = Special Function Register (SFR bank 1, range FFF00h - FFFFFh)&lt;br /&gt;
 ESR = Extended Special Function Register (SFR bank 2, range F0000h - F0806h)&lt;br /&gt;
 /   = active low (ground to enable, pull to power supply to disable)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  TP&lt;br /&gt;
!  Pin&lt;br /&gt;
!  Port&lt;br /&gt;
!  Purpose&lt;br /&gt;
|-&lt;br /&gt;
| TP79&lt;br /&gt;
| A8&lt;br /&gt;
| EVdd&lt;br /&gt;
| Digital voltage source input (positive)&lt;br /&gt;
|-&lt;br /&gt;
| TP74&lt;br /&gt;
| E7&lt;br /&gt;
| /RESET&lt;br /&gt;
| Resets the MCU when grounded, but is also used when reprogramming&lt;br /&gt;
|-&lt;br /&gt;
| TP75&lt;br /&gt;
| E8&lt;br /&gt;
| FLMD0&lt;br /&gt;
| Flash mode(?) used when reprogramming with external programmer&lt;br /&gt;
|-&lt;br /&gt;
| TP76&lt;br /&gt;
| E6&lt;br /&gt;
| TOOL1&lt;br /&gt;
| Used when using an ICE or debugger&lt;br /&gt;
|-&lt;br /&gt;
| TP77&lt;br /&gt;
| D6&lt;br /&gt;
| TOOL0&lt;br /&gt;
| Multipurpose pin for reprogramming and debugging&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| A7&lt;br /&gt;
| SCL0 / P6.0&lt;br /&gt;
| DSi-side I2C SCL&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| A6&lt;br /&gt;
| SDA0 / P6.1&lt;br /&gt;
| DSi-side I2C SDA&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| E1&lt;br /&gt;
| SCL1 / ESR[510h].und&lt;br /&gt;
| 3DS-side SCL&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| F1&lt;br /&gt;
| SDA1 / ESR[510h].und&lt;br /&gt;
| 3DS-side SDA&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| F7&lt;br /&gt;
| /P0.1&lt;br /&gt;
| SocReset_n (one of the two SoC reset signals)&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| G7&lt;br /&gt;
| /P0.0&lt;br /&gt;
| SocReset_n (one of the two SoC reset signals)&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| /P3.0&lt;br /&gt;
| Unknown. Probably resets something, as it&#039;s poked in a similar pattern to the SoC reset signals.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P5.0&lt;br /&gt;
| Toggles something (poked in conjunction with reset signals)&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P2.0&lt;br /&gt;
| HOME button&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P4.3&lt;br /&gt;
| Charging LED(?)&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P5.1&lt;br /&gt;
| Charger &amp;quot;button&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P7.0&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P2.4&lt;br /&gt;
| BatteryChargeState (?)&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P7.3&lt;br /&gt;
| Power button&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P7.4&lt;br /&gt;
| WiFi button&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| P7.6&lt;br /&gt;
| External IRQ (MCU --&amp;gt; SoC)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CODEC ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;font-family:Monospace;text-align:center;width:100%;table-layout:fixed;width:36%;&amp;quot;&lt;br /&gt;
| || style=&amp;quot;background: #476b6b&amp;quot; | 4? || style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || || style=&amp;quot;background: #cc9900&amp;quot; | 3? || style=&amp;quot;background: #cc9900&amp;quot; | 0? || style=&amp;quot;background: #336600&amp;quot; | ? || style=&amp;quot;background: #336600&amp;quot; | ? || || style=&amp;quot;background: #bbbbbb&amp;quot; | G ||&lt;br /&gt;
|-&lt;br /&gt;
|   || style=&amp;quot;background: #476b6b&amp;quot; | 3? || style=&amp;quot;background: #476b6b&amp;quot; | 5? || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || style=&amp;quot;background: #cc9900&amp;quot; | 1? || style=&amp;quot;background: #336600&amp;quot; | CSx || style=&amp;quot;background: #336600&amp;quot; | ? || || style=&amp;quot;background: #bbbbbb&amp;quot; | G ||&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #476b6b&amp;quot; | 2? || style=&amp;quot;background: #476b6b&amp;quot; | 0? || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || style=&amp;quot;background: #cc9900&amp;quot; | 2? || style=&amp;quot;background: #336600&amp;quot; | CSy || || || || SPEAKER1&lt;br /&gt;
|-&lt;br /&gt;
| TOUCH || TOUCH || style=&amp;quot;background: #476b6b&amp;quot; | 1? || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || SPEAKER1&lt;br /&gt;
|-&lt;br /&gt;
| TOUCH || TOUCH || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || ||&lt;br /&gt;
|-&lt;br /&gt;
| CPAD || CPAD || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || SPEAKER2&lt;br /&gt;
|-&lt;br /&gt;
| MIC || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || SPEAKER2&lt;br /&gt;
|-&lt;br /&gt;
| JACK_R || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || || || || || style=&amp;quot;background: #bbbbbb&amp;quot; | G&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #d9ffb3&amp;quot; | 3v3 || || || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || || || || JACK_L || || style=&amp;quot;background: #bbbbbb&amp;quot; | G || style=&amp;quot;background: #bbbbbb&amp;quot; | G ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== LCD (old3DS bottom) ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Pin number&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 01&lt;br /&gt;
| -6V&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 02&lt;br /&gt;
| 12V&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 03&lt;br /&gt;
| CLK&lt;br /&gt;
| Pixel clock&lt;br /&gt;
|-&lt;br /&gt;
| 04&lt;br /&gt;
| /HBL&lt;br /&gt;
| Horizontal blank (low while blanking)&lt;br /&gt;
|-&lt;br /&gt;
| 05&lt;br /&gt;
| /VBL&lt;br /&gt;
| Vertical blank (low while blanking)&lt;br /&gt;
|-&lt;br /&gt;
| 06&lt;br /&gt;
| 2v2&lt;br /&gt;
| Loopback of pin 07?&lt;br /&gt;
|-&lt;br /&gt;
| 07&lt;br /&gt;
| 2v2&lt;br /&gt;
| Content latch? Shorting this to ground or to pin 06 will &amp;quot;lock&amp;quot; the screen memory while still allowing the screen to refresh itself.&lt;br /&gt;
|-&lt;br /&gt;
| 08&lt;br /&gt;
| GND&lt;br /&gt;
| &amp;quot;chassi&amp;quot; ground&lt;br /&gt;
|-&lt;br /&gt;
| 09&lt;br /&gt;
| HCL&lt;br /&gt;
| Horizontal clock&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| GND&lt;br /&gt;
| &amp;quot;chassi&amp;quot; ground&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| BIAS 1&lt;br /&gt;
| Default ~ 4.5V - 4.8V; sets contrast&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| BIAS 2&lt;br /&gt;
| Usually matches BIAS 1; sets &amp;quot;flicker&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| ???&lt;br /&gt;
| Might be a transistor? Shorted to ground if off, 2.36V if on.&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 6V&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| ???&lt;br /&gt;
| Loopback of pin 14 ? Shorting this with pin 14 or ground will make the 3DS turn off with a harsh pop sound.&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 18&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
25&lt;br /&gt;
| RED 0&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
RED 7&lt;br /&gt;
| Red pixel bits&lt;br /&gt;
|-&lt;br /&gt;
| 26&lt;br /&gt;
| GND&lt;br /&gt;
| &amp;quot;chassi&amp;quot; ground&lt;br /&gt;
|-&lt;br /&gt;
| 27&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
34&lt;br /&gt;
| BLUE 7&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
BLUE 0&lt;br /&gt;
| Blue pixel bits&lt;br /&gt;
|-&lt;br /&gt;
| 35&lt;br /&gt;
| GND&lt;br /&gt;
| &amp;quot;chassi&amp;quot; ground&lt;br /&gt;
|-&lt;br /&gt;
| 36&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
43&lt;br /&gt;
| GREEN 7&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
GREEN 0&lt;br /&gt;
| Green pixel bits&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21578</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21578"/>
		<updated>2021-09-06T14:14:08Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Added info on DebugPad&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
&lt;br /&gt;
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.&lt;br /&gt;
&lt;br /&gt;
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| For bit0 and 1 values, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)&lt;br /&gt;
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)&lt;br /&gt;
  bit7: TWL MCU reg: Uses NAND&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery temperature (in Celcius?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: ??? (the off event for below bit)&lt;br /&gt;
  bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)&lt;br /&gt;
  bit26: ??? (???)&lt;br /&gt;
  bit27: ??? (???)&lt;br /&gt;
  bit28: ??? (???)&lt;br /&gt;
  bit29: ??? backlight on?&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]&lt;br /&gt;
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)&lt;br /&gt;
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.&lt;br /&gt;
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)&lt;br /&gt;
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
    - Resets TWL MCU i2c registers&lt;br /&gt;
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing &amp;amp; a PDN reg also possibly implements this function)&lt;br /&gt;
    - Asserts FCRAM_RESET (P3.0 = 0)&lt;br /&gt;
  bit4: signal that sleep mode is about to be entered (used by PTM)&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.&lt;br /&gt;
&lt;br /&gt;
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.&lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used in legacy mode to signal events for TWL MCU &amp;quot;emulation&amp;quot; (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].&lt;br /&gt;
  bit0: Signal TWL POWER button click&lt;br /&gt;
  bit1: Signal TWL reset&lt;br /&gt;
  bit2: Signal TWL power off&lt;br /&gt;
  bit3: Signal TWL battery low&lt;br /&gt;
  bit4: Signal TWL battery empty&lt;br /&gt;
  bit5: Signal TWL volume button click&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Writing 0x72 (&#039;r&#039;) resets the MCU, but this is stubbed on retail?&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| rw&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm). &lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn&#039;t deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank address (index) select&lt;br /&gt;
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.&lt;br /&gt;
&lt;br /&gt;
  byte 0: bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit3 = &amp;quot;LgyNativeResolution&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;&lt;br /&gt;
  byte 1: Legacy LCD data&lt;br /&gt;
  bytes 2 and 3: Local Friend Code counter&lt;br /&gt;
  bytes 4 and 5: UUID clock sequence&lt;br /&gt;
  bytes 6 and 7: Unused&lt;br /&gt;
  bytes 8 to 175: Playtime data for legacy titles&lt;br /&gt;
  bytes 176 to 188: Playtime data&lt;br /&gt;
  bytes 188 to 199: Unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| d(200)&lt;br /&gt;
| rw&lt;br /&gt;
| Free register bank, data is read from/written to here.&lt;br /&gt;
&lt;br /&gt;
Accessing N bytes of this register increments the selected index by N.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]&lt;br /&gt;
  byte 0x01: PMIC vendor code&lt;br /&gt;
  byte 0x02: Battery vendor code&lt;br /&gt;
  byte 0x03: MGIC version (major?)&lt;br /&gt;
  byte 0x04: MGIC version (minor?)&lt;br /&gt;
  byte 0x05: RCOMP(?)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
LCD controllers for main/sub displays, most likely.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Width&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_IN/CMD_RESULT1&lt;br /&gt;
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_RESULT0&lt;br /&gt;
| Read result &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].&lt;br /&gt;
&lt;br /&gt;
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=HID_Shared_Memory&amp;diff=21577</id>
		<title>HID Shared Memory</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=HID_Shared_Memory&amp;diff=21577"/>
		<updated>2021-09-06T14:03:28Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: /* Entry format */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the format of the [[HID_Services|HID]] shared memory.&lt;br /&gt;
&lt;br /&gt;
The data for each of the below entries(PAD state, circle-pad, touch-screen, etc) is originally written by the HID module at different times per frame.&lt;br /&gt;
&lt;br /&gt;
size: 0x2b0 (System-version v4.4 - [[9.0.0-20]])&lt;br /&gt;
&lt;br /&gt;
=Offset 0x0=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| Before the above tick-count field is updated, that value is copied into this field. Therefore, this contains the tick-count for the previous time that entry index0 in the below array was updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x4&lt;br /&gt;
| Index in the following array which was last updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| 0x4&lt;br /&gt;
| Current [[PAD]] state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| Raw circle-pad info.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x80&lt;br /&gt;
| Array of 8 entries, where each entry(see below) is 0x10-bytes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
HID module first updates index0, then index1, and so on. When updating the array when the index is already 7, the index is reset to 0.&lt;br /&gt;
&lt;br /&gt;
==PAD State==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| A&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| B&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Select&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Start&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Right&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Left&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Up&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Down&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| R&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| L&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| X&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Y&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| Inverted value of [[GPIO_Services|GPIO]] bit0.&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| Inverted value of [[GPIO_Services|GPIO]] bit14.&lt;br /&gt;
|-&lt;br /&gt;
| 28&lt;br /&gt;
| Circle pad right (X &amp;gt;= 41)&lt;br /&gt;
|-&lt;br /&gt;
| 29&lt;br /&gt;
| Circle pad left (X &amp;lt;= -41)&lt;br /&gt;
|-&lt;br /&gt;
| 30&lt;br /&gt;
| Circle pad up (Y &amp;gt;= 41)&lt;br /&gt;
|-&lt;br /&gt;
| 31&lt;br /&gt;
| Circle pad down (Y &amp;lt;= -41)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Bit set = button pressed, bit clear = button not pressed. Bit28-31 only apply to the PAD fields in the array entries. Bit28-31 are set by HID module depending on data from a codec command, these are not included with the [[PAD]] register itself. Likewise for Bit12-13, except these are set depending on data from a GPIO command, and are likely used with the sharedmem PAD-state field too(this was originally implemented in an old HID module version / initial module version).&lt;br /&gt;
&lt;br /&gt;
==Entry format==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| Current PAD state.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| PAD state for buttons which were pressed(bitmasks which changed from value 0 to value 1) since the last HID update.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x4&lt;br /&gt;
| PAD state for buttons which were released(bitmasks which changed from value 1 to value 0) since the last HID update.&lt;br /&gt;
|-&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x4&lt;br /&gt;
| This stores circle-pad info: the low s16 is the X coordinate, the high s16 is the Y coordinate. The circle-pad center is approximately 0 for these fields. The range for these fields is approximately: -0x9C(bottom/left) - 0x9C(top/right).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Offset 0xA8=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| Before the above tick-count field is updated, that value is copied into this field. Therefore, this contains the tick-count for the previous time that entry index0 in the below array was updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x4&lt;br /&gt;
| Index in the following array which was last updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x8&lt;br /&gt;
| Touch-screen entry, which contains the raw coordinate data prior to being converted to pixel coordinates.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8 Touch-screen entries, containing pixel coordinates.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This 0x60-byte region stores state for touch-screen related info. Each touch-screen entry is all-zero when the touch-screen is not being touched.&lt;br /&gt;
&lt;br /&gt;
HID module first updates index0, then index1, and so on. When updating the array when the index is already 7, the index is reset to 0.&lt;br /&gt;
&lt;br /&gt;
==Entry format==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x2&lt;br /&gt;
| X coordinate.&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x2&lt;br /&gt;
| Y coordinate.&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| The u8 at +0 here is 0x0 when this entry doesn&#039;t contain any actual data, value 0x1 indicates that this entry contains actual data.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Offset 0x108=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| Before the above tick-count field is updated, that value is copied into this field. Therefore, this contains the tick-count for the previous time that entry index0 in the below array was updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x4&lt;br /&gt;
| Index in the following array which was last updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x6&lt;br /&gt;
| Current accelerometer state entry, contains the raw accelerometer output data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x30&lt;br /&gt;
| Array containing 8 accelerometer entries. Each entry when updated contains accelerometer data converted from the above entry.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The size of this region is 0x50-bytes. This contains the accelerometer state. The data stored under these entries is loaded from [[MCU|MCUHID]] service commands.&lt;br /&gt;
&lt;br /&gt;
HID module first updates index0, then index1, and so on. When updating the array when the index is already 7, the index is reset to 0.&lt;br /&gt;
&lt;br /&gt;
==Entry format==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x2&lt;br /&gt;
| s16. X?&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x2&lt;br /&gt;
| s16. Y?&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x2&lt;br /&gt;
| s16. Z?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Offset 0x158=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| Before the above tick-count field is updated, that value is copied into this field. Therefore, this contains the tick-count for the previous time that entry index0 in the below array was updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x4&lt;br /&gt;
| Index in the following array which was last updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x6&lt;br /&gt;
| Current gyroscope state entry.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0xC0&lt;br /&gt;
| Array containing 32 gyroscrope entries.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The size of this region is 0xE0-bytes. Initially this contains 0xFF/0x00 bytes. This contains the gyroscope state. The [[I2C]] gyroscope device is used for this.&lt;br /&gt;
&lt;br /&gt;
HID module first updates index0, then index1, and so on. When updating the array when the index is already 31, the index is reset to 0.&lt;br /&gt;
&lt;br /&gt;
==Entry format==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x2&lt;br /&gt;
| s16 X/roll(This the raw byte-swapped data from I2C, for the entry at +0x18 and the array entries).&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x2&lt;br /&gt;
| Entry at +0x18: s16 Y/pitch. Array entries: s16 Z/yaw.&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x2&lt;br /&gt;
| Entry at +0x18: s16 Z/yaw. Array entries: s16 Y/pitch, from the raw byte-swapped I2C data.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Offset 0x238=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x8&lt;br /&gt;
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| Before the above tick-count field is updated, that value is copied into this field. Therefore, this contains the tick-count for the previous time that entry index0 in the below array was updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x4&lt;br /&gt;
| Index in the following array which was last updated by HID module.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x60&lt;br /&gt;
| Array containing 8 entries, see below.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The size of this region is 0x78-bytes. Initially this contains 0xFF/0x00 bytes. The data stored under these entries is for the DebugPad. [[I2C]] deviceid 12 is used for this.&lt;br /&gt;
&lt;br /&gt;
This doesn&#039;t have any HID service commands for enabling/disabling this. HID module only updates this state when reading the DebugPad state from hardware via the service command was successful. Since the DebugPad hardware is not available on retail units, this sharedmem section is not updated by HID module on retail units.&lt;br /&gt;
&lt;br /&gt;
HID module first updates index0, then index1, and so on. When updating the array when the index is already 7, the index is reset to 0.&lt;br /&gt;
&lt;br /&gt;
==Entry format==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Relative offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x2&lt;br /&gt;
| Keys held&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x2&lt;br /&gt;
| Keys just pressed&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x2&lt;br /&gt;
| Keys just released&lt;br /&gt;
|-&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| Left Stick X (range roughly -32 to +31)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| Left Stick Y (range roughly -32 to +31)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| Right Stick X (range roughly -16 to +15)&lt;br /&gt;
|-&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| Right Stick Y (range roughly -16 to +15)&lt;br /&gt;
|-&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x2&lt;br /&gt;
| Padding, not written by HID module.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: there is code in hid sysmodule where holding +, -, DPAD Left, and A will recallibrate the sticks to zero.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21516</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21516"/>
		<updated>2021-04-18T14:38:57Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Add 2DS info to &amp;quot;scanline double&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| H-total (V-total on not physically rotated screens).&lt;br /&gt;
| 12bits.&lt;br /&gt;
&lt;br /&gt;
Setting this value too low will make the screen not be able to sync any pixels other than a single one from the wrong location. The lowest the screen can handle is 0x1C2, at 0x1C1 the display loses a few scanlines worth of pixel clock (though not noticable).&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HBlank timer(?)&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| V-total (H-total on not physically rotated screens).&lt;br /&gt;
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| VBlank timer(?)&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| Horizontal position counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Horizontal scanline (HBlank) counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: H(Blank?) IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable?* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
On a 2DS, it seems to have no effect on the top part of the display, and on the bottom screen it just shifts the framebuffer to the right two pixels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=21515</id>
		<title>Hardware calibration</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=21515"/>
		<updated>2021-04-17T00:52:57Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: added MCU slider cfgblk ID&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=File format=&lt;br /&gt;
The file consists out of a 0x200 big header (though the actual header is only 0x30 bytes, the rest is zerofilled), plus data whose size is indicated in the header.&lt;br /&gt;
&lt;br /&gt;
==Header==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| Magic &amp;quot;CCAL&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x4&lt;br /&gt;
| Data size, always 0x7D0&lt;br /&gt;
|-&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| Model version (?)&lt;br /&gt;
|-&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| CAL revision (incremented each time the CAL file is updated)&lt;br /&gt;
|-&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x2&lt;br /&gt;
| [[#Aging masks|Bitmask of successful Aging tests]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x20&lt;br /&gt;
| Signature of the data section.&lt;br /&gt;
&lt;br /&gt;
HMACSHA256 is used always except in the below cases where SHA256 is used:&lt;br /&gt;
  - devunits&lt;br /&gt;
  - PARTNER-DEBUGGER&lt;br /&gt;
  - PARTNER-CAPTURE&lt;br /&gt;
  - the SNAKE counterparts of the above&lt;br /&gt;
  - SNAKE-IS-DEBUGGER&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x1D0&lt;br /&gt;
| Zerofilled, padding for the 512byte block size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Aging masks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| FCRAM&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| LCD &amp;quot;flicker&amp;quot;/contrast (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Camera&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Touch panel (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Circle pad (analog stick)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Codec&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Gyroscope&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| RTC&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Accelerometer&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Surround&lt;br /&gt;
|-&lt;br /&gt;
| A&lt;br /&gt;
| Adaptive BackLight (ABL)&lt;br /&gt;
|-&lt;br /&gt;
| B&lt;br /&gt;
| 3D screen (ULCD)&lt;br /&gt;
|-&lt;br /&gt;
| C&lt;br /&gt;
| Backlight PWM&lt;br /&gt;
|-&lt;br /&gt;
| D&lt;br /&gt;
| Analog stick A (???)&lt;br /&gt;
|-&lt;br /&gt;
| E&lt;br /&gt;
| Camera extensions&lt;br /&gt;
|-&lt;br /&gt;
| F&lt;br /&gt;
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Data blocks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| 0x10&lt;br /&gt;
| 00040000&lt;br /&gt;
| [[#Touch|Touch panel]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Circle_pad|Circle pad]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 2*&lt;br /&gt;
| 00050000&lt;br /&gt;
| [[#Screen_flicker|Display panel contrast]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| 1*&lt;br /&gt;
| ???&lt;br /&gt;
| [[#RTC|RTC]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 1*&lt;br /&gt;
| ???&lt;br /&gt;
| DSPRAM related&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x8A&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Camera_position|Camera position]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| 0x12&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Gyro|Gyroscope]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xD0&lt;br /&gt;
| 0xC&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Accel|Accelerometer]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xE0&lt;br /&gt;
| 0x134&lt;br /&gt;
| ???&lt;br /&gt;
| [[#CDC|Codec]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x218&lt;br /&gt;
| 0x06&lt;br /&gt;
| ???&lt;br /&gt;
| [[#PIT|Programmable Infrared Transmitter (PIT)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x220&lt;br /&gt;
| 0x214&lt;br /&gt;
| ???&lt;br /&gt;
| [[#3D_filters|3D filters]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x440&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ABL|Adaptive BackLight / Power saving mode]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x470&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x4A0&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x00050002&lt;br /&gt;
| [[#BLPWM|Backlight PWM]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E0&lt;br /&gt;
| 0x18&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Circle_pad_extra|Circle pad extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x500&lt;br /&gt;
| 0xC&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x510&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x540&lt;br /&gt;
| 0x08&lt;br /&gt;
| 0x00120000&lt;br /&gt;
| [[#MCU|MCU]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x550&lt;br /&gt;
| 0x02&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ULCD_delay|3D screen (ULCD) delay]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x560&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Microphone_echo_cancel|Microphone echo cancellation]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x570&lt;br /&gt;
| 0x10C&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ABL_extra|Power saving mode (ABL) extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x680&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#CStick|CStick (Right stick)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x690&lt;br /&gt;
| 0x18&lt;br /&gt;
| ???&lt;br /&gt;
| [[#QTM|Quad Tracking Module (QTM)]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Data block formats=&lt;br /&gt;
&lt;br /&gt;
==Touch==&lt;br /&gt;
Used for mapping touch ADC values to display pixel co-ordinates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
[4096, 4096] --&amp;gt; [320, 240]&lt;br /&gt;
&lt;br /&gt;
[RawX, RawY] --&amp;gt; [PointX, PointY]&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually [PointX0, PointY0] is placed around 25% from the top-left corner, and the same for [PointX1, PointY1] except 25% from the bottom-right corner.&lt;br /&gt;
This offsetting is needed because the touch film starts to distort outside of that rectangle, which would skew the touch results near the center of the screen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 RawX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 RawY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 PointX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 PointY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 RawX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 RawY1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 PointX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PointY1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad==&lt;br /&gt;
&lt;br /&gt;
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 CenterX&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Raw analog values corresponding to zero input position&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 CenterY&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Screen flicker==&lt;br /&gt;
&lt;br /&gt;
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 FlickerTop&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Contrast voltage&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| u8 FlickerBottom&lt;br /&gt;
|-&lt;br /&gt;
| 2*&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Inline checksum&lt;br /&gt;
| Checksum low byte, NOT THIS[0]&lt;br /&gt;
|-&lt;br /&gt;
| 3*&lt;br /&gt;
| Checksum high byte, THIS[1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==RTC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 CompensationValue&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 1*&lt;br /&gt;
| &lt;br /&gt;
| Checksum byte, NOT THIS[0]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Camera position==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 flags&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float scale&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float RotationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| float RotationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| float ViewAngleRight&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| float ViewAngleLeft&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| float ChartDistance(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| float CameraDistance&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s16 ImageWidth&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s16 ImageHeight&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| u8 reserved[0x10]&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| u8 ???[0x40]&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| s16 aeBaseTarget(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x82&lt;br /&gt;
| s16 kRL&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 kGL&lt;br /&gt;
|-&lt;br /&gt;
| 0x86&lt;br /&gt;
| s16 kBL&lt;br /&gt;
|-&lt;br /&gt;
| 0x88&lt;br /&gt;
| s16 ccmPosition&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Gyro==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 ZeroX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 PlusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 MinusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ZeroY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 PlusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 ZeroZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PlusZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 MinusZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Accel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 OffsetX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 OffsetY&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 OffsetZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 ScaleZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CDC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 DriverGainHP&lt;br /&gt;
| Headphone gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 DriverGainSP&lt;br /&gt;
| Speaker gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u8 AnalogVolumeHP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| u8 AnalogVolumeSP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s8 ShutterVolume[2]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| u8 MicrophoneBias&lt;br /&gt;
| Capacitive microphone bias voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x07&lt;br /&gt;
| u8 QuickCharge&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u8 PGA_GAIN&lt;br /&gt;
| ??? (microphone gain)&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| u8 reserved[3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 FilterHP32[3*5]&lt;br /&gt;
| Headphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s16 FilterHP47[3*5]&lt;br /&gt;
| Headphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| s16 FilterSP32[3*5]&lt;br /&gt;
| Speaker filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x66&lt;br /&gt;
| s16 FilterSP47[3*5]&lt;br /&gt;
| Speaker filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 FilterMic32[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| s16 FilterMic47[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xF4&lt;br /&gt;
| s16 FilterFree[(1+2)+((1+4)*5)]&lt;br /&gt;
| Unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x12C&lt;br /&gt;
| u8 AnalogInterval&lt;br /&gt;
|-&lt;br /&gt;
| 0x12D&lt;br /&gt;
| u8 AnalogStabilize&lt;br /&gt;
|-&lt;br /&gt;
| 0x12E&lt;br /&gt;
| u8 AnalogPrecharge&lt;br /&gt;
|-&lt;br /&gt;
| 0x12F&lt;br /&gt;
| u8 AnalogSense&lt;br /&gt;
|-&lt;br /&gt;
| 0x130&lt;br /&gt;
| u8 AnalogDebounce&lt;br /&gt;
|-&lt;br /&gt;
| 0x131&lt;br /&gt;
| u8 Analog_XP_Pullup&lt;br /&gt;
|-&lt;br /&gt;
| 0x132&lt;br /&gt;
| u8 YM_Driver&lt;br /&gt;
| ??? (circle-pad or touch panel related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x133&lt;br /&gt;
| u8 reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==PIT==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 VisibleFactor&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 IRFactor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==3D filters==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 SpecialFilter[0x100]&lt;br /&gt;
|-&lt;br /&gt;
| 0x200&lt;br /&gt;
| u32 IIRSurroundFilter[5]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 DitherPattern&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 StartX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 StartY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u16 SizeX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| u16 SizeY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 GTH_Ratio&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| u8 DitherMode&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| u8 MinRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| u8 MaxRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| u8 MinGTH&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 MinMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x13&lt;br /&gt;
| u8 ExMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| u8 inertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
| u8 LutListRS[9]&lt;br /&gt;
|-&lt;br /&gt;
| 0x1E&lt;br /&gt;
| u8 reserved[2]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==BLPWM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float coefficient[3][3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| u8 NumLevels&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| u8 padding&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| u16 brightnesses[7];&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| u16 BaseDivisor&lt;br /&gt;
|-&lt;br /&gt;
| 0x36&lt;br /&gt;
| u16 MinimumBrightnessHw&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 MaxX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 MaxY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 MinY&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 type&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 unknown_padding[6]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MCU==&lt;br /&gt;
&lt;br /&gt;
Somewhat misleading, these values are actually used for clamping the MCU&#039;s raw slider readings to comprehensible values.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 SVR2_Min&lt;br /&gt;
| Raw 3D volume slider values &amp;lt;= this map to 3D slider value 0.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 SVR2_Max&lt;br /&gt;
| Raw 3D volume slider values &amp;gt;= this map to 3D slider value 1.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 VolumeSliderMin&lt;br /&gt;
| Written to MCU reg 0x58. Volume slider values &amp;lt;= this map to volume value 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 VolumeSliderMax&lt;br /&gt;
| Written to MCU reg 0x59. Volume slider values &amp;gt;= this map to volume value 0x3F&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ULCD delay==&lt;br /&gt;
&lt;br /&gt;
There is a delay between switching the parallax barrier, and adjusting the backlight.&lt;br /&gt;
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.&lt;br /&gt;
&lt;br /&gt;
This is needed only to prevent epillepsy from analog jitter causing unwanted mode switches, and both values are usually always set to 1 or 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 To2D&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 To3D&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Microphone echo cancel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s8 params[8]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 MaxInertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 pad&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 PWM_CNT_EX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| u32 Histogram1&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u32 Histogram2&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| u32 adjust[0x40]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CStick==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 ThinningCountX(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 ThinningCountY(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 reserved[3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==QTM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float DivisorAtZero (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float HorizontalAngle&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float OptimalDistance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Reading=&lt;br /&gt;
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the &amp;lt;code&amp;gt;i2c:EEP&amp;lt;/code&amp;gt; service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.&lt;br /&gt;
Otherwise attempt is made to read &amp;lt;code&amp;gt;CTRNAND:/ro/sys/HWCAL(0|1).dat&amp;lt;/code&amp;gt; instead.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21489</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21489"/>
		<updated>2021-02-24T09:13:38Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Corrected info about interlace control&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| H-total (V-total on not physically rotated screens).&lt;br /&gt;
| 12bits.&lt;br /&gt;
&lt;br /&gt;
Setting this value too low will make the screen not be able to sync any pixels other than a single one from the wrong location. The lowest the screen can handle is 0x1C2, at 0x1C1 the display loses a few scanlines worth of pixel clock (though not noticable).&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HBlank timer(?)&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| V-total (H-total on not physically rotated screens).&lt;br /&gt;
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| VBlank timer(?)&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| Horizontal position counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Horizontal scanline (HBlank) counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: H(Blank?) IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5-4&lt;br /&gt;
| Framebuffer scanline output mode (interlace config)&lt;br /&gt;
&lt;br /&gt;
 0 - A  (output image as normal)&lt;br /&gt;
 1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)&lt;br /&gt;
 2 - AB (interlace framebuffer A and framebuffer B)&lt;br /&gt;
 3 - BA (same as above, but the line from framebuffer B is outputted first)&lt;br /&gt;
&lt;br /&gt;
0 is used by bottom screen at all times.&lt;br /&gt;
1 is used by the top screen in 2D mode.&lt;br /&gt;
2 is used by top screen in 3D mode.&lt;br /&gt;
3 goes unused in userland.&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Scan doubling enable* (used by top screen)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The weird thing about scan doubling, is that it works different between the bottom and top LCD. On the bottom LCD, it doubles the number of outputted pixels (so the same pixel is outputted twice, effectively doing column doubling). However on the top screen, it does scanline doubling instead. Considering that the bottom screen&#039;s table doesn&#039;t work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and scan doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen in userland.&lt;br /&gt;
&lt;br /&gt;
If only AB interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only scan doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and scan doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21488</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21488"/>
		<updated>2021-02-24T07:39:33Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Document undefined color format&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| H-total (V-total on not physically rotated screens).&lt;br /&gt;
| 12bits.&lt;br /&gt;
&lt;br /&gt;
Setting this value too low will make the screen not be able to sync any pixels other than a single one from the wrong location. The lowest the screen can handle is 0x1C2, at 0x1C1 the display loses a few scanlines worth of pixel clock (though not noticable).&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HBlank timer(?)&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| V-total (H-total on not physically rotated screens).&lt;br /&gt;
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| VBlank timer(?)&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| Horizontal position counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Horizontal scanline (HBlank) counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: H(Blank?) IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Interlacing enable (interleaves framebuffers A and B; used by top screen in 3D mode)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Line doubling enable (used by top screen in 2D mode)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and line doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen.&lt;br /&gt;
&lt;br /&gt;
If only interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only line doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and line doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
Color formats 5, 6, and 7 are blocked by gsp, but they behave as pixel-doubled RGBA8 (not line doubling, but instead the same pixel is output twice) if used outside of userland.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21487</id>
		<title>GPU/External Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=GPU/External_Registers&amp;diff=21487"/>
		<updated>2021-02-24T06:07:23Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Document interlacing and line doubling properly&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the address range accessible from the ARM11, used to configure the basic GPU functionality. For information about the internal registers used for 3D rendering, see [[GPU/Internal Registers]].&lt;br /&gt;
&lt;br /&gt;
== Map ==&lt;br /&gt;
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! User VA&lt;br /&gt;
! PA&lt;br /&gt;
! Length&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00004&lt;br /&gt;
| 0x10400004&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00010&lt;br /&gt;
| 0x10400010&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill1]] &amp;quot;PSC0&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00020&lt;br /&gt;
| 0x10400020&lt;br /&gt;
| 16&lt;br /&gt;
| [[#Memory Fill|Memory Fill2]] &amp;quot;PSC1&amp;quot;&lt;br /&gt;
| GX command 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00030&lt;br /&gt;
| 0x10400030&lt;br /&gt;
| 4&lt;br /&gt;
| VRAM bank control&lt;br /&gt;
| Bits 8-11 = bank[i] disabled; other bits are unused&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00034&lt;br /&gt;
| 0x10400034&lt;br /&gt;
| 4&lt;br /&gt;
| GPU Busy&lt;br /&gt;
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00050&lt;br /&gt;
| 0x10400050&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x22221200 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00054&lt;br /&gt;
| 0x10400054&lt;br /&gt;
| 4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFF2 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000C0&lt;br /&gt;
| 0x104000C0&lt;br /&gt;
| 4&lt;br /&gt;
| Backlight control&lt;br /&gt;
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00400&lt;br /&gt;
| 0x10400400&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC0&amp;quot; (top screen)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00500&lt;br /&gt;
| 0x10400500&lt;br /&gt;
| 0x100&lt;br /&gt;
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] &amp;quot;PDC1&amp;quot; (bottom)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| 0x10400C00&lt;br /&gt;
| ?&lt;br /&gt;
| [[#Transfer_Engine|Transfer Engine]] &amp;quot;DMA&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;| 0x1EF01000/0x10401000 - 0x1EF01C00/0x10401C00 maps to [[GPU/Internal_Registers|GPU internal registers]]. These registers are usually not read/written directly here, but are written using the command list interface below (corresponding to the GPUREG_CMDBUF_* internal registers)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01000&lt;br /&gt;
| 0x10401000&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0 on GPU init and before the Command List is used&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF01080&lt;br /&gt;
| 0x10401080&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0x12345678 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010C0&lt;br /&gt;
| 0x104010C0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 0xFFFFFFF0 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF010D0&lt;br /&gt;
| 0x104010D0&lt;br /&gt;
| 0x4&lt;br /&gt;
| ?&lt;br /&gt;
| Writes 1 on GPU init.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF014??&lt;br /&gt;
| 0x104014??&lt;br /&gt;
| 0x14&lt;br /&gt;
| &amp;quot;PPF&amp;quot; ?&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| 0x104018E0&lt;br /&gt;
| 0x14&lt;br /&gt;
| [[#Command_List|Command List]] &amp;quot;P3D&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Memory Fill ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  User VA&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X0&lt;br /&gt;
| Buffer start physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X4&lt;br /&gt;
| Buffer end physaddr &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000X8&lt;br /&gt;
| Fill value&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF000XC&lt;br /&gt;
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Memory fills are used to initialize buffers in memory with a given value, similar to memset. A memory fill is triggered by setting bit0 in the control register. Doing so aborts any running memory fills on that filling unit. Upon completion, the hardware unsets bit0 and sets bit1 and fires interrupt PSC0.&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].&lt;br /&gt;
&lt;br /&gt;
== LCD Source Framebuffer Setup ==&lt;br /&gt;
&lt;br /&gt;
All of these registers must be accessed with 32bit operations regardless of the registers&#039; actual bit size.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Offset&lt;br /&gt;
! Name&lt;br /&gt;
! Comments&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| H-total (V-total on not physically rotated screens).&lt;br /&gt;
| 12bits.&lt;br /&gt;
&lt;br /&gt;
Setting this value too low will make the screen not be able to sync any pixels other than a single one from the wrong location. The lowest the screen can handle is 0x1C2, at 0x1C1 the display loses a few scanlines worth of pixel clock (though not noticable).&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| HBlank timer(?)&lt;br /&gt;
| Seems to determine the horizontal blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal - HDisp&amp;lt;/code&amp;gt; will make the displayed image misaligned to the right.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; seems to make the horizontal synchronization never happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| ?&lt;br /&gt;
| must be &amp;gt;= REG#0x08&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| Window X start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical X co-ordinate relative to its front porch end.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| Window X end (LgyFb)&lt;br /&gt;
| Window X start + window width - 1 is written here to set the end display scan pixel offset.&lt;br /&gt;
&lt;br /&gt;
Outside of LgyFb it seems to offset the screen to the left if this value is high enough, but can glitch out the syncing on the bottom screen. High enough values will make the screen skip too many &amp;quot;pixels&amp;quot;. If this value is higher or equal to *some value* (aka. if less than one pixel per line is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| Window Y start (LgyFb)&lt;br /&gt;
| Offsets the viewing window on the display&#039;s physical Y co-ordinate relative to the first visible scanline.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| Low: Window Y end (LgyFb)&lt;br /&gt;
High: ???&lt;br /&gt;
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.&lt;br /&gt;
&lt;br /&gt;
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn&#039;t really seem to do anything useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
??? extra pixels get inserted in the first displayed scanline and thus the image gets shifted to the right. Seems to make horizontal syncing a bit glitchy. If a HSync occurs, the pixel data is suspended until the first pixel is supposed to be displayed, then the pixel stream will continue where it left off until a delayed HSync gets processed relative to the pixel data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| V-total (H-total on not physically rotated screens).&lt;br /&gt;
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| VBlank timer(?)&lt;br /&gt;
| Seems to determine the vertical blanking interval.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting this to lower than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will cut off the top &amp;lt;code&amp;gt;VTotal - VDisp - thisvalue&amp;lt;/code&amp;gt; lines.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;VTotal - VDisp&amp;lt;/code&amp;gt; will make the image be pushed downwards with the overscan color visible.&lt;br /&gt;
&lt;br /&gt;
Setting this to higher than &amp;lt;code&amp;gt;HTotal&amp;lt;/code&amp;gt; will make the GPU skip vertical pixel data synchronization (hence filling the screen with the rest of the pixel data past the given screen framebuffer size). Also will skip &amp;lt;code&amp;gt;thisvalue + somevalue - HTotal&amp;lt;/code&amp;gt; lines into the &amp;quot;global&amp;quot; pixel buffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| VTotal&lt;br /&gt;
| Total amount of vertical scanlines in the pixel buffer, must be bigger than *an unknown blanking-like value*. If this value is less than VDisp then the last two scanlines will be repeated interlaced until VDisp is reached.&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| VDisp(?)&lt;br /&gt;
| Total amonut of vertical scanlines displayed (only for top screen it seems like). If this value is less than VTotal then the rest of the scanlines will not be updated on the screen, so those will slowly fade out. Must be bigger than *an unknown blanking-like value*, otherwise an underflow will happen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| Vertical data offset(?)&lt;br /&gt;
| ??? Seems to offset the screen upwards if this value is high enough. If this value is higher or equal to *some value* (aka. if less than one scanline is displayed on the screen) then the screen will lose synchronization.&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| ???&lt;br /&gt;
| similar functionality to 0x10&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| ???&lt;br /&gt;
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren&#039;t writable.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
| Overscan filler color&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| Horizontal position counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x54&lt;br /&gt;
| Horizontal scanline (HBlank) counter&lt;br /&gt;
| read-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x5C&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: framebuffer width&lt;br /&gt;
high u16: framebuffer height??? (seems to be unused)&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: timing data(?)&lt;br /&gt;
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)&lt;br /&gt;
|-&lt;br /&gt;
| 0x64&lt;br /&gt;
| ???&lt;br /&gt;
| low u16: unknown&lt;br /&gt;
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| Framebuffer A first address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x6C&lt;br /&gt;
| Framebuffer A second address&lt;br /&gt;
| For top screen, this is the left eye 3D framebuffer.&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| Framebuffer format&lt;br /&gt;
| Bit0-15: framebuffer format, bit16-31: unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x74&lt;br /&gt;
| PDC control&lt;br /&gt;
| Bit 0: Enable display controller.&lt;br /&gt;
Bit 8: H(Blank?) IRQ mask (0 = enabled).&lt;br /&gt;
Bit 9: VBlank IRQ mask (0 = enabled).&lt;br /&gt;
Bit 10: Error IRQ mask? (0 = enabled).&lt;br /&gt;
Bit 16: Output enable?&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| Framebuffer select and status&lt;br /&gt;
| Bit 0: Next framebuffer to display (after VBlank).&lt;br /&gt;
Bit 4: Currently displaying framebuffer?&lt;br /&gt;
Bit 8: Reset FIFO?&lt;br /&gt;
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.&lt;br /&gt;
Bit 17: VBlank IRQ status/ack.&lt;br /&gt;
Bit 18: Error IRQ status/ack?&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| Color lookup table index select&lt;br /&gt;
| 8bits, write-only&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| Color lookup table indexed element&lt;br /&gt;
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR)  &lt;br /&gt;
Accessing this register will increase the index register by one&lt;br /&gt;
|-&lt;br /&gt;
| 0x90&lt;br /&gt;
| Framebuffer stride&lt;br /&gt;
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).&lt;br /&gt;
|-&lt;br /&gt;
| 0x94&lt;br /&gt;
| Framebuffer B first address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|-&lt;br /&gt;
| 0x98&lt;br /&gt;
| Framebuffer B second address&lt;br /&gt;
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer format ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 2-0&lt;br /&gt;
| Color format&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Interlacing enable (interleaves framebuffers A and B; used by top screen in 3D mode)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Line doubling enable (used by top screen in 2D mode)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9-8&lt;br /&gt;
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.&lt;br /&gt;
|-&lt;br /&gt;
| 15-10&lt;br /&gt;
| Unused?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
GSP module only allows the LCD stereoscopy to be enabled when bit5=1 and bit6=0 here. When GSP module updates this register, GSP module will automatically disable the stereoscopy if those bits are not set for enabling stereoscopy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When both interlacing and line doubling are disabled, the full resolution of the top screen (240x800) can be utilized if the PDC registers are updated to accomodate this higher resolution. GSP contains tables for this mode (gsp mode == 1). GSP automatically applies this mode if both bit5 and bit6 are cleared. This is also the default, and the only valid mode for the bottom screen.&lt;br /&gt;
&lt;br /&gt;
If only interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.&lt;br /&gt;
It&#039;s unknown how to control this, but some other PDC registers control if interlacing should be done by true interleaving (both framebuffers are treated as 240x400), or skipping lines (both framebuffers are treated as 240x800)&lt;br /&gt;
&lt;br /&gt;
If only line doubling is enabled, gsp detects it as a request to switch back to 2D mode for the top screen (gsp mode == 0). This is also the default mode for the top screen.&lt;br /&gt;
&lt;br /&gt;
Both interlacing and line doubling can&#039;t be enabled in usermode, but it works as expected in baremetal.&lt;br /&gt;
&lt;br /&gt;
=== Framebuffer color formats ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| GL_RGBA8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| GL_RGB8_OES&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| GL_RGB565_OES&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| GL_RGB5_A1_OES&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| GL_RGBA4_OES&lt;br /&gt;
|}&lt;br /&gt;
Color components are laid out in reverse byte order, with the most significant bits used first (i.e. non-24-bit pixels are stored as a little-endian values). For instance, a raw data stream of two GL_RGB565_OES pixels looks like GGGBBBBB RRRRRGGG GGGBBBBB RRRRRGGG.&lt;br /&gt;
&lt;br /&gt;
== Transfer Engine ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C00&lt;br /&gt;
| Input physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C04&lt;br /&gt;
| Output physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C08&lt;br /&gt;
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C0C&lt;br /&gt;
| DisplayTransfer input width and height.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C10&lt;br /&gt;
| Transfer flags. (See below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C14&lt;br /&gt;
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C18&lt;br /&gt;
|  Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C1C&lt;br /&gt;
|  ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C20&lt;br /&gt;
| TextureCopy total amount of data to copy, in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C24&lt;br /&gt;
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF00C28&lt;br /&gt;
| TextureCopy output line width and gap.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These registers are used by [[GSP_Shared_Memory|GX command]] 3 and 4. For cmd4, *0x1EF00C18 |= 1 is used instead of just writing value 1. The DisplayTransfer registers are only used if bit 3 of the flags is unset and ignored otherwise. The TextureCopy registers are likewise only used if bit 3 is set, and ignored otherwise.&lt;br /&gt;
&lt;br /&gt;
==== Flags Register - 0x1EF00C10 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| When set, the framebuffer data is flipped vertically.&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled-&amp;gt;linear when unset.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| This bit is required when the output width is less than the input width for the hardware to properly crop the lines, otherwise the output will be mis-aligned.&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Uses a TextureCopy mode transfer. See below for details.&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Don&#039;t perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.&lt;br /&gt;
|-&lt;br /&gt;
| 7-6&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 10-8&lt;br /&gt;
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 14-12&lt;br /&gt;
| Output framebuffer color format&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Use 32x32 block tiling mode, instead of the usual 8x8 one. Output dimensions must be multiples of 32, even if cropping with bit 2 set above.&lt;br /&gt;
|-&lt;br /&gt;
| 17-23&lt;br /&gt;
| Not writable&lt;br /&gt;
|-&lt;br /&gt;
| 24-25&lt;br /&gt;
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid&lt;br /&gt;
|-&lt;br /&gt;
| 31-26&lt;br /&gt;
| Not writable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== TextureCopy ===&lt;br /&gt;
&lt;br /&gt;
When bit 3 of the control register is set, the hardware performs a TextureCopy-mode transfer. In this mode, all other bits of the control register (except for bit 2, which still needs to be set correctly) and the regular dimension registers are ignored, and no format conversions are done. Instead, it performs a raw data copy from the source to the destination, but with a configurable gap between lines. The total amount of bytes to copy is specified in the size register, and the hardware loops reading lines from the input and writing them to the output until this amount is copied. The &amp;quot;gap&amp;quot; specified in the input/output dimension register is the number of chunks to skip after each &amp;quot;width&amp;quot; chunks of the input/output, and is NOT counted towards the total size of the transfer.&lt;br /&gt;
&lt;br /&gt;
By correctly calculating the input and output gap sizes it is possible to use this functionality to copy arbitrary sub-rectangles between differently-sized framebuffers or textures, which is one of its main uses over a regular no-conversion DisplayTransfer. When copying tiled textures/framebuffers it&#039;s important to remember that the contents of a tile are laid out sequentially in memory, and so this should be taken into account when calculating the transfer parameters.&lt;br /&gt;
&lt;br /&gt;
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.&lt;br /&gt;
&lt;br /&gt;
== Command List ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register address&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E0&lt;br /&gt;
| Buffer size in bytes &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018E8&lt;br /&gt;
| Buffer physical address &amp;gt;&amp;gt; 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EF018F0&lt;br /&gt;
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].&lt;br /&gt;
&lt;br /&gt;
== Framebuffers ==&lt;br /&gt;
These LCD framebuffers normally contain the last rendered frames from the GPU. The framebuffers are drawn from left-to-right, instead of top-to-bottom.(Thus the beginning of the framebuffer is drawn starting at the left side of the screen)&lt;br /&gt;
&lt;br /&gt;
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider&#039;s state, however when the 3D slider is set to &amp;quot;off&amp;quot; the 3D effect is disabled. Normally when the 3D slider&#039;s state is set to &amp;quot;off&amp;quot; the left/right framebuffer addresses are set to the same physical address. When the 3D effect is disabled and the left/right framebuffers are set to separate addresses, the LCD seems to alternate between displaying the left/right framebuffer each frame.&lt;br /&gt;
&lt;br /&gt;
==== Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00400 = 0x1C2&lt;br /&gt;
* 0x1EF00404 = 0xD1&lt;br /&gt;
* 0x1EF00408 = 0x1C1&lt;br /&gt;
* 0x1EF0040C = 0x1C1&lt;br /&gt;
* 0x1EF00410 = 0&lt;br /&gt;
* 0x1EF00414 = 0xCF&lt;br /&gt;
* 0x1EF00418 = 0xD1&lt;br /&gt;
* 0x1EF0041C = 0x1C501C1&lt;br /&gt;
* 0x1EF00420 = 0x10000&lt;br /&gt;
* 0x1EF00424 = 0x19D&lt;br /&gt;
* 0x1EF00428 = 2&lt;br /&gt;
* 0x1EF0042C = 0x1C2&lt;br /&gt;
* 0x1EF00430 = 0x1C2&lt;br /&gt;
* 0x1EF00434 = 0x1C2&lt;br /&gt;
* 0x1EF00438 = 1&lt;br /&gt;
* 0x1EF0043C = 2&lt;br /&gt;
* 0x1EF00440 = 0x1960192&lt;br /&gt;
* 0x1EF00444 = 0&lt;br /&gt;
* 0x1EF00448 = 0&lt;br /&gt;
* 0x1EF0045C = 0x19000F0&lt;br /&gt;
* 0x1EF00460 = 0x1c100d1&lt;br /&gt;
* 0x1EF00464 = 0x1920002&lt;br /&gt;
* 0x1EF00470 = 0x80340&lt;br /&gt;
* 0x1EF0049C = 0&lt;br /&gt;
&lt;br /&gt;
==== More Init Values from nngxInitialize for Top Screen ====&lt;br /&gt;
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer&lt;br /&gt;
* 0x1EF00494 = 0x18300000&lt;br /&gt;
* 0x1EF00498 = 0x18300000&lt;br /&gt;
* 0x1EF00478 = 1, doesn&#039;t stay 1, read as 0&lt;br /&gt;
* 0x1EF00474 = 0x10501&lt;br /&gt;
&lt;br /&gt;
[[Category:GPU]]&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=21343</id>
		<title>Hardware calibration</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=Hardware_calibration&amp;diff=21343"/>
		<updated>2020-11-09T00:14:00Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Clean up some of the non-descriptive and/or wrong variable names&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=File format=&lt;br /&gt;
The file consists out of a 0x200 big header (though the actual header is only 0x30 bytes, the rest is zerofilled), plus data whose size is indicated in the header.&lt;br /&gt;
&lt;br /&gt;
==Header==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| Magic &amp;quot;CCAL&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x4&lt;br /&gt;
| Data size, always 0x7D0&lt;br /&gt;
|-&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| Model version (?)&lt;br /&gt;
|-&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| CAL revision (incremented each time the CAL file is updated)&lt;br /&gt;
|-&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x2&lt;br /&gt;
| [[#Aging masks|Bitmask of successful Aging tests]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x20&lt;br /&gt;
| Signature of the data section.&lt;br /&gt;
&lt;br /&gt;
HMACSHA256 is used always except in the below cases where SHA256 is used:&lt;br /&gt;
  - devunits&lt;br /&gt;
  - PARTNER-DEBUGGER&lt;br /&gt;
  - PARTNER-CAPTURE&lt;br /&gt;
  - the SNAKE counterparts of the above&lt;br /&gt;
  - SNAKE-IS-DEBUGGER&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x1D0&lt;br /&gt;
| Zerofilled, padding for the 512byte block size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Aging masks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| FCRAM&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| LCD &amp;quot;flicker&amp;quot;/contrast (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Camera&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Touch panel (always successful)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Circle pad (analog stick)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Codec&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Gyroscope&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| RTC&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Accelerometer&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Surround&lt;br /&gt;
|-&lt;br /&gt;
| A&lt;br /&gt;
| Adaptive BackLight (ABL)&lt;br /&gt;
|-&lt;br /&gt;
| B&lt;br /&gt;
| 3D screen (ULCD)&lt;br /&gt;
|-&lt;br /&gt;
| C&lt;br /&gt;
| Backlight PWM&lt;br /&gt;
|-&lt;br /&gt;
| D&lt;br /&gt;
| Analog stick A (???)&lt;br /&gt;
|-&lt;br /&gt;
| E&lt;br /&gt;
| Camera extensions&lt;br /&gt;
|-&lt;br /&gt;
| F&lt;br /&gt;
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Data blocks==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| 0x10&lt;br /&gt;
| 00040000&lt;br /&gt;
| [[#Touch|Touch panel]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Circle_pad|Circle pad]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 2*&lt;br /&gt;
| 00050000&lt;br /&gt;
| [[#Screen_flicker|Display panel contrast]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| 1*&lt;br /&gt;
| ???&lt;br /&gt;
| [[#RTC|RTC]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 1*&lt;br /&gt;
| ???&lt;br /&gt;
| DSPRAM related&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x8A&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Camera_position|Camera position]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| 0x12&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Gyro|Gyroscope]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xD0&lt;br /&gt;
| 0xC&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Accel|Accelerometer]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xE0&lt;br /&gt;
| 0x134&lt;br /&gt;
| ???&lt;br /&gt;
| [[#CDC|Codec]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x218&lt;br /&gt;
| 0x06&lt;br /&gt;
| ???&lt;br /&gt;
| [[#PIT|Programmable Infrared Transmitter (PIT)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x220&lt;br /&gt;
| 0x214&lt;br /&gt;
| ???&lt;br /&gt;
| [[#3D_filters|3D filters]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x440&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ABL|Adaptive BackLight / Power saving mode]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x470&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x4A0&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x00050002&lt;br /&gt;
| [[#BLPWM|Backlight PWM]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E0&lt;br /&gt;
| 0x18&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Circle_pad_extra|Circle pad extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x500&lt;br /&gt;
| 0xC&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x510&lt;br /&gt;
| 0x20&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x540&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#MCU|MCU]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x550&lt;br /&gt;
| 0x02&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ULCD_delay|3D screen (ULCD) delay]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x560&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#Microphone_echo_cancel|Microphone echo cancellation]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x570&lt;br /&gt;
| 0x10C&lt;br /&gt;
| ???&lt;br /&gt;
| [[#ABL_extra|Power saving mode (ABL) extra]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x680&lt;br /&gt;
| 0x08&lt;br /&gt;
| ???&lt;br /&gt;
| [[#CStick|CStick (Right stick)]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x690&lt;br /&gt;
| 0x18&lt;br /&gt;
| ???&lt;br /&gt;
| [[#QTM|Quad Tracking Module (QTM)]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Data block formats=&lt;br /&gt;
&lt;br /&gt;
==Touch==&lt;br /&gt;
Used for mapping touch ADC values to display pixel co-ordinates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
[4096, 4096] --&amp;gt; [320, 240]&lt;br /&gt;
&lt;br /&gt;
[RawX, RawY] --&amp;gt; [PointX, PointY]&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually [PointX0, PointY0] is placed around 25% from the top-left corner, and the same for [PointX1, PointY1] except 25% from the bottom-right corner.&lt;br /&gt;
This offsetting is needed because the touch film starts to distort outside of that rectangle, which would skew the touch results near the center of the screen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 RawX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 RawY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 PointX0&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 PointY0&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 RawX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 RawY1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 PointX1&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PointY1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad==&lt;br /&gt;
&lt;br /&gt;
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 CenterX&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Raw analog values corresponding to zero input position&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 CenterY&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Screen flicker==&lt;br /&gt;
&lt;br /&gt;
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 FlickerTop&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Contrast voltage&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| u8 FlickerBottom&lt;br /&gt;
|-&lt;br /&gt;
| 2*&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;| Inline checksum&lt;br /&gt;
| Checksum low byte, NOT THIS[0]&lt;br /&gt;
|-&lt;br /&gt;
| 3*&lt;br /&gt;
| Checksum high byte, THIS[1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==RTC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| u8 CompensationValue&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 1*&lt;br /&gt;
| &lt;br /&gt;
| Checksum byte, NOT THIS[0]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Camera position==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 flags&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float scale&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float RotationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| float RotationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
| float ViewAngleRight&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| float ViewAngleLeft&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| float ChartDistance(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| float CameraDistance&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s16 ImageWidth&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s16 ImageHeight&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| u8 reserved[0x10]&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| u8 ???[0x40]&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
| s16 aeBaseTarget(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x82&lt;br /&gt;
| s16 kRL&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 kGL&lt;br /&gt;
|-&lt;br /&gt;
| 0x86&lt;br /&gt;
| s16 kBL&lt;br /&gt;
|-&lt;br /&gt;
| 0x88&lt;br /&gt;
| s16 ccmPosition&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Gyro==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 ZeroX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 PlusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 MinusX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ZeroY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 PlusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinusY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 ZeroZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 PlusZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 MinusZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Accel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 OffsetX&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 OffsetY&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 OffsetZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 ScaleZ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CDC==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 DriverGainHP&lt;br /&gt;
| Headphone gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 DriverGainSP&lt;br /&gt;
| Speaker gain&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u8 AnalogVolumeHP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| u8 AnalogVolumeSP&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s8 ShutterVolume[2]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| u8 MicrophoneBias&lt;br /&gt;
| Capacitive microphone bias voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x07&lt;br /&gt;
| u8 QuickCharge&lt;br /&gt;
| (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u8 PGA_GAIN&lt;br /&gt;
| ??? (microphone gain)&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| u8 reserved[3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 FilterHP32[3*5]&lt;br /&gt;
| Headphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s16 FilterHP47[3*5]&lt;br /&gt;
| Headphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| s16 FilterSP32[3*5]&lt;br /&gt;
| Speaker filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x66&lt;br /&gt;
| s16 FilterSP47[3*5]&lt;br /&gt;
| Speaker filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0x84&lt;br /&gt;
| s16 FilterMic32[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 32728.49Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xBC&lt;br /&gt;
| s16 FilterMic47[(1+2)+((1+4)*5)]&lt;br /&gt;
| Microphone filter for 47605Hz sampling rate&lt;br /&gt;
|-&lt;br /&gt;
| 0xF4&lt;br /&gt;
| s16 FilterFree[(1+2)+((1+4)*5)]&lt;br /&gt;
| Unknown&lt;br /&gt;
|-&lt;br /&gt;
| 0x12C&lt;br /&gt;
| u8 AnalogInterval&lt;br /&gt;
|-&lt;br /&gt;
| 0x12D&lt;br /&gt;
| u8 AnalogStabilize&lt;br /&gt;
|-&lt;br /&gt;
| 0x12E&lt;br /&gt;
| u8 AnalogPrecharge&lt;br /&gt;
|-&lt;br /&gt;
| 0x12F&lt;br /&gt;
| u8 AnalogSense&lt;br /&gt;
|-&lt;br /&gt;
| 0x130&lt;br /&gt;
| u8 AnalogDebounce&lt;br /&gt;
|-&lt;br /&gt;
| 0x131&lt;br /&gt;
| u8 Analog_XP_Pullup&lt;br /&gt;
|-&lt;br /&gt;
| 0x132&lt;br /&gt;
| u8 YM_Driver&lt;br /&gt;
| ??? (circle-pad or touch panel related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x133&lt;br /&gt;
| u8 reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==PIT==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 VisibleFactor&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 IRFactor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==3D filters==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u16 SpecialFilter[0x100]&lt;br /&gt;
|-&lt;br /&gt;
| 0x200&lt;br /&gt;
| u32 IIRSurroundFilter[5]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u32 DitherPattern&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 StartX&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 StartY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u16 SizeX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| u16 SizeY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 GTH_Ratio&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| u8 DitherMode&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| u8 MinRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| u8 MaxRS&lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| u8 MinGTH&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 MinMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x13&lt;br /&gt;
| u8 ExMax (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| u8 inertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
| u8 LutListRS[9]&lt;br /&gt;
|-&lt;br /&gt;
| 0x1E&lt;br /&gt;
| u8 reserved[2]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==BLPWM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float coefficient[3][3]&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| u8 NumLevels&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| u8 padding&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| u16 brightnesses[7];&lt;br /&gt;
|-&lt;br /&gt;
| 0x34&lt;br /&gt;
| u16 BaseDivisor&lt;br /&gt;
|-&lt;br /&gt;
| 0x36&lt;br /&gt;
| u16 MinimumBrightnessHw&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Circle pad extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float ScaleX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float ScaleY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s16 MaxX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s16 MinX&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s16 MaxY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s16 MinY&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| s16 type&lt;br /&gt;
|-&lt;br /&gt;
| 0x12&lt;br /&gt;
| u8 unknown_padding[6]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MCU==&lt;br /&gt;
&lt;br /&gt;
Somewhat misleading, these values are actually used for clamping the MCU&#039;s raw slider readings to comprehensible values.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s16 SVR2_Min&lt;br /&gt;
| Raw 3D volume slider values &amp;lt;= this map to 3D slider value 0.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| s16 SVR2_Max&lt;br /&gt;
| Raw 3D volume slider values &amp;gt;= this map to 3D slider value 1.0&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| s16 VolumeSliderMin&lt;br /&gt;
| Written to MCU reg 0x58. Volume slider values &amp;lt;= this map to volume value 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0x06&lt;br /&gt;
| s16 VolumeSliderMax&lt;br /&gt;
| Written to MCU reg 0x59. Volume slider values &amp;gt;= this map to volume value 0x3F&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ULCD delay==&lt;br /&gt;
&lt;br /&gt;
There is a delay between switching the parallax barrier, and adjusting the backlight.&lt;br /&gt;
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.&lt;br /&gt;
&lt;br /&gt;
This is needed only to prevent epillepsy from analog jitter causing unwanted mode switches, and both values are usually always set to 1 or 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 To2D&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 To3D&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Microphone echo cancel==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s8 params[8]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==ABL extra==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 MaxInertia&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 pad&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 PWM_CNT_EX&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| u32 Histogram1&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| u32 Histogram2&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| u32 adjust[0x40]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CStick==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| u8 ThinningCountX(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| u8 ThinningCountY(???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| u16 reserved[3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==QTM==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Declaration&lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| float DivisorAtZero (???)&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| float TranslationX&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| float TranslationY&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| float RotationZ&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| float HorizontalAngle&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| float OptimalDistance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Reading=&lt;br /&gt;
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the &amp;lt;code&amp;gt;i2c:EEP&amp;lt;/code&amp;gt; service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.&lt;br /&gt;
Otherwise attempt is made to read &amp;lt;code&amp;gt;CTRNAND:/ro/sys/HWCAL(0|1).dat&amp;lt;/code&amp;gt; instead.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=MTX_Registers&amp;diff=21160</id>
		<title>MTX Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=MTX_Registers&amp;diff=21160"/>
		<updated>2020-04-01T21:07:20Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Update MTX bits and interrupts&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These registers are responsible for controlling how framebuffer data can be DMA&#039;d from the DS GPU, and also for configuring the upscaling matrix.&lt;br /&gt;
&lt;br /&gt;
=Registers=&lt;br /&gt;
&lt;br /&gt;
The physical address can be calculated by subtracting 0xEB00000 from the virtual address.&lt;br /&gt;
&lt;br /&gt;
==Control==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  VAddress&lt;br /&gt;
!  Name&lt;br /&gt;
!  Width&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1x000&lt;br /&gt;
| [[#MTX_CNT|MTX_CNT]]&lt;br /&gt;
| 4&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1x004&lt;br /&gt;
| [[#MTX_SIZE|MTX_SIZE]]&lt;br /&gt;
| 4&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1x008&lt;br /&gt;
| [[#MTX_ACK|MTX_ACK]]&lt;br /&gt;
| 4&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1x00C&lt;br /&gt;
| [[#MTX_IE|MTX_IE]]&lt;br /&gt;
| 4&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1x020&lt;br /&gt;
| ???&lt;br /&gt;
| 4&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Matrix unit==&lt;br /&gt;
&lt;br /&gt;
There are two matrix units, one at +0x200 for vertical (Y) scaling, and the other one at +0x300 for horizontal (X) scaling.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  VAddress&lt;br /&gt;
!  Name&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1xn00&lt;br /&gt;
| KRN_WIDTH&lt;br /&gt;
| 4&lt;br /&gt;
| Kernel width - 1 is written here, 1 &amp;lt;= width &amp;lt;= 8&lt;br /&gt;
&lt;br /&gt;
This decides how many pixels are written each batch.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1xn04&lt;br /&gt;
| KRN_PATTERN_BITS&lt;br /&gt;
| 4&lt;br /&gt;
| If the corresponding bit for the current batch iteration index is set then a new pixel is read.&lt;br /&gt;
&lt;br /&gt;
The amount of set bits determine how many pixels are read each batch. Any bit indexes past KRN_WIDTH are ignored.&lt;br /&gt;
&lt;br /&gt;
This value is 8 bits, but it has to be written with a 32bit write.&lt;br /&gt;
|-&lt;br /&gt;
| 0x1EC1xn40&lt;br /&gt;
| KRN_MTX&lt;br /&gt;
| 0xC0&lt;br /&gt;
| int kerneldata[6][8]; - matrix data is written here, height is always 6&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Descriptions=&lt;br /&gt;
&lt;br /&gt;
==MTX_CNT==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit(s)&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Enable bit&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Enable vertical matrix&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Enable horizontal matrix&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 8-9&lt;br /&gt;
| Input pixel mode? 0 = 4byte color, 1 = 3byte color, 2 = 2byte color, 3 = 2byte color&lt;br /&gt;
|-&lt;br /&gt;
| 10-11&lt;br /&gt;
| Output framebuffer rotation: 0 = normal, 1 = 90° CW (right), 2 = 180° CW (upside down, not mirrored), 3 = 270° CW (left)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| Output tiling for use with the GPU. When set, the output width and height must be a multiple of 8.&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| Start bit (setting this will eventually raise MTX interrupt 0)&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| Data still available flag (?)&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MTX_SIZE==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit(s)&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-8&lt;br /&gt;
| Output framebuffer width - 1 is written here, 1 &amp;lt;= width &amp;lt;= 512&lt;br /&gt;
|-&lt;br /&gt;
| 16-25&lt;br /&gt;
| Output framebuffer height - 1 is written here, 1 &amp;lt;= height &amp;lt;= 512&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MTX_ACK==&lt;br /&gt;
&lt;br /&gt;
Reading this register will return pending interrupts.&lt;br /&gt;
Writing this register will acknowledge pending interrupts where the bits are set.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bit(s)&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| FIFO ready (signal to start DMA)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| FIFO overrun(?) (occurs if DMA is too slow)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| FIFO underrun(?) (occurs on VBlank)&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==MTX_IE==&lt;br /&gt;
&lt;br /&gt;
Interrupt Enable for the above interrupts.&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21132</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21132"/>
		<updated>2019-12-28T18:24:18Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Clarify where each gyroscope is used (although missing old3DSXL and non-XL new3DS)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (2DS, new3DSXL, new2DSXL)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| 2bit value, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (seems to be power management related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: ??? (the off event for below bit)&lt;br /&gt;
  bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)&lt;br /&gt;
  bit26: ??? (???)&lt;br /&gt;
  bit27: ??? (???)&lt;br /&gt;
  bit28: ??? (???)&lt;br /&gt;
  bit29: ??? backlight on?&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: reboot (unused?)&lt;br /&gt;
  bit2: reboot (used by mcu sysmodule and LgyBg)&lt;br /&gt;
  bit3: used by LgyBg to power off, causes hangs in 3DS-mode&lt;br /&gt;
  bit4: an mcu::RTC command uses this, seems to do something with the watchdog&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F. &lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| ??? switches up input bits from &amp;lt;code&amp;gt;0123456--&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;12-0435-&amp;lt;/code&amp;gt; then writes them to REG[0x5D] (&amp;lt;code&amp;gt;0xFFC02&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| ??&lt;br /&gt;
| wo&lt;br /&gt;
| ??? Seems to be stubbed, just returns the written value from the write handler function.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| ??&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.&lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| s&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Looping queue register&lt;br /&gt;
Writing to first byte resets the queue position to the nth element&lt;br /&gt;
Reading from this register causes the values to shift up by `readsize-1`(needs confirmation) bytes after returning `readsize-1` bytes from the top of the stack (first byte is read-only, so is always zero)&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| ds(0x100)&lt;br /&gt;
| rw&lt;br /&gt;
| Writing to this register pushes values on top of register 0x60&#039;s stack. Reading from this register doesn&#039;t advance the stack.&lt;br /&gt;
The first byte is used to store flags for managing FIRM/NS state - bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;. This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn&#039;t seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
LCD controllers for main/sub displays, most likely.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Width&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_IN/CMD_RESULT1&lt;br /&gt;
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_RESULT0&lt;br /&gt;
| Read result &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the DebugPad device, see [[HID_Shared_Memory|here]].&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21127</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21127"/>
		<updated>2019-12-25T22:49:57Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Added more gyro info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (old3DS?)&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope (new3DS?)&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| 2bit value, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (seems to be power management related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: ??? (the off event for below bit)&lt;br /&gt;
  bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)&lt;br /&gt;
  bit26: ??? (???)&lt;br /&gt;
  bit27: ??? (???)&lt;br /&gt;
  bit28: ??? (???)&lt;br /&gt;
  bit29: ??? backlight on?&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: reboot (unused?)&lt;br /&gt;
  bit2: reboot (used by mcu sysmodule and LgyBg)&lt;br /&gt;
  bit3: used by LgyBg to power off, causes hangs in 3DS-mode&lt;br /&gt;
  bit4: an mcu::RTC command uses this, seems to do something with the watchdog&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F. &lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| ??? switches up input bits from &amp;lt;code&amp;gt;0123456--&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;12-0435-&amp;lt;/code&amp;gt; then writes them to REG[0x5D] (&amp;lt;code&amp;gt;0xFFC02&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| ??&lt;br /&gt;
| wo&lt;br /&gt;
| ??? Seems to be stubbed, just returns the written value from the write handler function.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| ??&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.&lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| s&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Looping queue register&lt;br /&gt;
Writing to first byte resets the queue position to the nth element&lt;br /&gt;
Reading from this register causes the values to shift up by `readsize-1`(needs confirmation) bytes after returning `readsize-1` bytes from the top of the stack (first byte is read-only, so is always zero)&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| ds(0x100)&lt;br /&gt;
| rw&lt;br /&gt;
| Writing to this register pushes values on top of register 0x60&#039;s stack. Reading from this register doesn&#039;t advance the stack.&lt;br /&gt;
The first byte is used to store flags for managing FIRM/NS state - bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;. This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn&#039;t seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
LCD controllers for main/sub displays, most likely.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Width&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_IN/CMD_RESULT1&lt;br /&gt;
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_RESULT0&lt;br /&gt;
| Read result &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 11 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the DebugPad device, see [[HID_Shared_Memory|here]].&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21108</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21108"/>
		<updated>2019-11-30T09:46:30Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: Fix tilt sensor typo and increased info verbosity&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| 2bit value, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (seems to be power management related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: ??? (the off event for below bit)&lt;br /&gt;
  bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)&lt;br /&gt;
  bit26: ??? (???)&lt;br /&gt;
  bit27: ??? (???)&lt;br /&gt;
  bit28: ??? (???)&lt;br /&gt;
  bit29: ??? backlight on?&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: reboot (unused?)&lt;br /&gt;
  bit2: reboot (used by mcu sysmodule and LgyBg)&lt;br /&gt;
  bit3: used by LgyBg to power off, causes hangs in 3DS-mode&lt;br /&gt;
  bit4: an mcu::RTC command uses this, seems to do something with the watchdog&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F. &lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| ??? switches up input bits from &amp;lt;code&amp;gt;0123456--&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;12-0435-&amp;lt;/code&amp;gt; then writes them to REG[0x5D] (&amp;lt;code&amp;gt;0xFFC02&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| ??&lt;br /&gt;
| wo&lt;br /&gt;
| ??? Seems to be stubbed, just returns the written value from the write handler function.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| ??&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.&lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| s&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held up vertically&lt;br /&gt;
| rotated left 90° like a steering wheel&lt;br /&gt;
| rotated right 90° like a steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat on the desk with the screen facing up&lt;br /&gt;
| held up vertically&lt;br /&gt;
| held up vertically with screen facing upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Looping queue register&lt;br /&gt;
Writing to first byte resets the queue position to the nth element&lt;br /&gt;
Reading from this register causes the values to shift up by `readsize-1`(needs confirmation) bytes after returning `readsize-1` bytes from the top of the stack (first byte is read-only, so is always zero)&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| ds(0x100)&lt;br /&gt;
| rw&lt;br /&gt;
| Writing to this register pushes values on top of register 0x60&#039;s stack. Reading from this register doesn&#039;t advance the stack.&lt;br /&gt;
The first byte is used to store flags for managing FIRM/NS state - bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;. This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn&#039;t seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
LCD controllers for main/sub displays, most likely.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Width&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_IN/CMD_RESULT1&lt;br /&gt;
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_RESULT0&lt;br /&gt;
| Read result &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the DebugPad device, see [[HID_Shared_Memory|here]].&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
	<entry>
		<id>https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21107</id>
		<title>I2C Registers</title>
		<link rel="alternate" type="text/html" href="https://www.3dbrew.org/w/index.php?title=I2C_Registers&amp;diff=21107"/>
		<updated>2019-11-30T09:39:30Z</updated>

		<summary type="html">&lt;p&gt;MarcusD: s/gyro/tilt sensor/gi&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Registers =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Old3DS&lt;br /&gt;
!  Name&lt;br /&gt;
!  Address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Used by&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_DATA&lt;br /&gt;
| 0x10161000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C1_CNT]]&lt;br /&gt;
| 0x10161001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_CNTEX&lt;br /&gt;
| 0x10161002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C1_SCL&lt;br /&gt;
| 0x10161004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 1 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_DATA&lt;br /&gt;
| 0x10144000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C2_CNT]]&lt;br /&gt;
| 0x10144001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_CNTEX&lt;br /&gt;
| 0x10144002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C2_SCL&lt;br /&gt;
| 0x10144004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 2 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_DATA&lt;br /&gt;
| 0x10148000&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| [[#I2C_CNT|I2C3_CNT]]&lt;br /&gt;
| 0x10148001&lt;br /&gt;
| 1&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_CNTEX&lt;br /&gt;
| 0x10148002&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: green&amp;quot; | Yes&lt;br /&gt;
| I2C3_SCL&lt;br /&gt;
| 0x10148004&lt;br /&gt;
| 2&lt;br /&gt;
| I2C bus 3 devices&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNT ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Stop (0=No, 1=Stop/last byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Start (0=No, 1=Start/first byte)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Ack Flag         (0=Error, 1=Okay)  (For DataRead: W, for DataWrite: R)&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Data Direction   (0=Write, 1=Read)&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Interrupt Enable (0=Disable, 1=Enable)&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Start/busy       (0=Ready, 1=Start/busy)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_CNTEX ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| ? Set to 2 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C_SCL ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  BIT&lt;br /&gt;
!  DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
| 0-5&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8-12&lt;br /&gt;
| ? Set to 5 normally.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= I2C Devices =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!   [[I2C_Registers|Device id]]&lt;br /&gt;
!   Device bus id&lt;br /&gt;
!   Device Write Address&lt;br /&gt;
!   Accessible via I2C [[I2C_Services|service]]&lt;br /&gt;
!   Device description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| Power management?(same device addr as the DSi power-management)&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0x7a&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera0?(same dev-addr as DSi cam0)&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 1&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| Camera1?(same dev-addr as DSi cam1)&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| 2&lt;br /&gt;
| 0x4a&lt;br /&gt;
| &amp;quot;i2c::MCU&amp;quot;&lt;br /&gt;
| MCU&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 2&lt;br /&gt;
| 0x78&lt;br /&gt;
| &amp;quot;i2c::CAM&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2c&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| 2&lt;br /&gt;
| 0x2e&lt;br /&gt;
| &amp;quot;i2c::LCD&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 2&lt;br /&gt;
| 0x44&lt;br /&gt;
| &amp;quot;i2c::DEB&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa6&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd0&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| Gyroscope&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| 3&lt;br /&gt;
| 0xd2&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa4&lt;br /&gt;
| &amp;quot;i2c::HID&amp;quot;&lt;br /&gt;
| DebugPad&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| 3&lt;br /&gt;
| 0x9a&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| IR&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| 3&lt;br /&gt;
| 0xa0&lt;br /&gt;
| &amp;quot;i2c::EEP&amp;quot;&lt;br /&gt;
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| 2&lt;br /&gt;
| 0xee&lt;br /&gt;
| &amp;quot;i2c::NFC&amp;quot;&lt;br /&gt;
| New3DS-only [[NFC_Services|NFC]]&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 1&lt;br /&gt;
| 0x40&lt;br /&gt;
| &amp;quot;i2c::QTM&amp;quot;&lt;br /&gt;
| New3DS-only [[QTM_Services|QTM]]&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| 3&lt;br /&gt;
| 0x54&lt;br /&gt;
| &amp;quot;i2c::IR&amp;quot;&lt;br /&gt;
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via &amp;quot;ir:rst&amp;quot;. This deviceid doesn&#039;t seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notice&#039;&#039;&#039;: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is &amp;gt;&amp;gt; 1.&lt;br /&gt;
&lt;br /&gt;
== Device 3 ==&lt;br /&gt;
  ro = read-only (writing is no-op)&lt;br /&gt;
  rw = read-write&lt;br /&gt;
  wo = write-only (reading will yield 00, FF, or unpredictable data)&lt;br /&gt;
&lt;br /&gt;
  d* = dynamic register (explaination below this table)&lt;br /&gt;
  s* = shared register (explaination below this table)&lt;br /&gt;
  ds = dynamic shared (explaination below this table)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  INFO&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x00&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version high&lt;br /&gt;
|-&lt;br /&gt;
| 0x01&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Version low&lt;br /&gt;
|-&lt;br /&gt;
| 0x02&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| 2bit value, writing will mask away/&amp;quot;acknowledge&amp;quot; the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog&lt;br /&gt;
  bit0: RTC clock value got reset to defaults&lt;br /&gt;
  bit1: Watchdog reset happened&lt;br /&gt;
|-&lt;br /&gt;
| 0x03&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Top screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x04&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Bottom screen Vcom&lt;br /&gt;
|-&lt;br /&gt;
| 0x05&lt;br /&gt;
- 0x07&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.&lt;br /&gt;
|-&lt;br /&gt;
| 0x08&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Raw 3D slider position&lt;br /&gt;
|-&lt;br /&gt;
| 0x09&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Volume slider state (0x00 - 0x3F)&lt;br /&gt;
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (seems to be power management related?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Battery percentage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0C&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0D&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| System voltage&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].&lt;br /&gt;
  bit01: ShellState&lt;br /&gt;
  bit03: AdapterState&lt;br /&gt;
  bit04: BatteryChargeState&lt;br /&gt;
  bit05: Bottom screen backlight on&lt;br /&gt;
  bit06: Top screen backlight on&lt;br /&gt;
  bit07: GPU on(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
- 0x13&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Received interrupt bitmask, see register 0x18 for possible values  &lt;br /&gt;
If no interrupt was received this register is 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x14&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Unused and unwritable byte :(&lt;br /&gt;
|-&lt;br /&gt;
| 0x15&lt;br /&gt;
- 0x17&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
- 0x1B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Interrupt mask for register 0x10 (0=enabled,1=disabled)&lt;br /&gt;
  bit00: Power button press (for 27 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit01: Power button held (for 375 &amp;quot;ticks&amp;quot;; the 3DS turns off regardless after a fixed time)&lt;br /&gt;
  bit02: HOME button press (for 5 &amp;quot;ticks&amp;quot;)&lt;br /&gt;
  bit03: HOME button release&lt;br /&gt;
  bit04: WiFi switch button&lt;br /&gt;
  bit05: Shell close&lt;br /&gt;
  bit06: Shell open&lt;br /&gt;
  bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)&lt;br /&gt;
  bit08: Charger removed&lt;br /&gt;
  bit09: Charger plugged in&lt;br /&gt;
  bit10: RTC alarm (when some conditions are met it&#039;s sent when the current day and month and year matches the current RTC time)&lt;br /&gt;
  bit11: ??? (accelerometer related)&lt;br /&gt;
  bit12: HID update&lt;br /&gt;
  bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)&lt;br /&gt;
  bit14: Battery stopped charging (independent of charger state)&lt;br /&gt;
  bit15: Battery started charging&lt;br /&gt;
Nonmaskable(?) interrupts&lt;br /&gt;
  bit16: ???&lt;br /&gt;
  bit17: ??? (opposite even for bit16)&lt;br /&gt;
  bit22: Volume slider position change&lt;br /&gt;
  bit23: ??? Register 0x0E update&lt;br /&gt;
  bit24: ??? (the off event for below bit)&lt;br /&gt;
  bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)&lt;br /&gt;
  bit26: ??? (???)&lt;br /&gt;
  bit27: ??? (???)&lt;br /&gt;
  bit28: ??? (???)&lt;br /&gt;
  bit29: ??? backlight on?&lt;br /&gt;
  bit30: bit set by mcu sysmodule&lt;br /&gt;
  bit31: bit set by mcu sysmodule&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C&lt;br /&gt;
- 0x1F&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused and unreferenced free RAM! Good for userdata.&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| System power control:&lt;br /&gt;
  bit0: power off&lt;br /&gt;
  bit1: reboot (unused?)&lt;br /&gt;
  bit2: reboot (used by mcu sysmodule and LgyBg)&lt;br /&gt;
  bit3: used by LgyBg to power off, causes hangs in 3DS-mode&lt;br /&gt;
  bit4: an mcu::RTC command uses this, seems to do something with the watchdog&lt;br /&gt;
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F. &lt;br /&gt;
|-&lt;br /&gt;
| 0x21&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| ??? switches up input bits from &amp;lt;code&amp;gt;0123456--&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;12-0435-&amp;lt;/code&amp;gt; then writes them to REG[0x5D] (&amp;lt;code&amp;gt;0xFFC02&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x22&lt;br /&gt;
| d&lt;br /&gt;
| wo&lt;br /&gt;
| Used to set LCD states&lt;br /&gt;
  bit0: don&#039;t push to LCDs&lt;br /&gt;
  bit1: push to LCDs&lt;br /&gt;
  bit2: bottom screen backlight off&lt;br /&gt;
  bit3: bottom screen backlight on&lt;br /&gt;
  bit4: top screen backlight off&lt;br /&gt;
  bit5: top screen backlight on&lt;br /&gt;
&lt;br /&gt;
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.&lt;br /&gt;
The rest of the bits are masked away.&lt;br /&gt;
|-&lt;br /&gt;
| 0x23&lt;br /&gt;
| ??&lt;br /&gt;
| wo&lt;br /&gt;
| ??? Seems to be stubbed, just returns the written value from the write handler function.&lt;br /&gt;
|-&lt;br /&gt;
| 0x24&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.&lt;br /&gt;
|-&lt;br /&gt;
| 0x25&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x26&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x27&lt;br /&gt;
| sd&lt;br /&gt;
| rw&lt;br /&gt;
| Raw volume slider state&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Brightness of the WiFi/Power LED&lt;br /&gt;
|-&lt;br /&gt;
| 0x29&lt;br /&gt;
| sd(5)&lt;br /&gt;
| ??&lt;br /&gt;
| Power mode indicator state (read-write)&lt;br /&gt;
  1 = forced default blue&lt;br /&gt;
  2 = sleep mode animation&lt;br /&gt;
  3 = &amp;quot;power off&amp;quot; mode&lt;br /&gt;
  4 = disable blue power LED and turn on red power LED&lt;br /&gt;
  5 = disable red power LED and turn on blue power LED&lt;br /&gt;
  6 = animate blue power LED off and flash red power LED&lt;br /&gt;
  anything else = automatic mode&lt;br /&gt;
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2A&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Camera LED state, 4bits wide,&lt;br /&gt;
  0, 3, 6-0xF = off&lt;br /&gt;
  1 = slowly blinking&lt;br /&gt;
  2 = constantly on&lt;br /&gt;
  3 = &amp;quot;TWL&amp;quot; mode&lt;br /&gt;
  4 = flash once&lt;br /&gt;
  5 = delay before changing to 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x2C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| 3D LED state, 4 bits wide&lt;br /&gt;
|-&lt;br /&gt;
| 0x2D&lt;br /&gt;
| 0x64&lt;br /&gt;
| wo&lt;br /&gt;
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It&#039;s possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it&#039;s considered write-only. Writing past the size of this register seems to do nothing.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2F&lt;br /&gt;
| s&lt;br /&gt;
| wo?&lt;br /&gt;
| ??? The write function for this register is stubbed.&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
- 0x36&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte &amp;amp; 0xF) + (10 * (byte &amp;gt;&amp;gt; 4)).&lt;br /&gt;
  byte 0: seconds&lt;br /&gt;
  byte 1: minutes&lt;br /&gt;
  byte 2: hours&lt;br /&gt;
  byte 3: current week (unused)&lt;br /&gt;
  byte 4: days&lt;br /&gt;
  byte 5: months&lt;br /&gt;
  byte 6: years&lt;br /&gt;
|-&lt;br /&gt;
| 0x37&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC time byte 7: leap year counter / &amp;quot;watch error correction&amp;quot; register (unused in code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
- 0x3C&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| RTC alarm registers&lt;br /&gt;
  byte 0: minutes&lt;br /&gt;
  byte 1: hours&lt;br /&gt;
  byte 2: day&lt;br /&gt;
  byte 3: month&lt;br /&gt;
  byte 4: year&lt;br /&gt;
|-&lt;br /&gt;
| 0x3B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.&lt;br /&gt;
|-&lt;br /&gt;
| 0x3D&lt;br /&gt;
0x3E&lt;br /&gt;
| ds&lt;br /&gt;
| ro&lt;br /&gt;
| RTC tick counter / &amp;quot;ITMC&amp;quot; (when resets to 0 the seconds increase)&lt;br /&gt;
Only reading 0x3D will update the in-RAM value&lt;br /&gt;
|-&lt;br /&gt;
| 0x3F&lt;br /&gt;
| s&lt;br /&gt;
| wo&lt;br /&gt;
| 2 bits&lt;br /&gt;
  bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)&lt;br /&gt;
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Index selector for register 0x44&lt;br /&gt;
|-&lt;br /&gt;
| 0x42&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused?&lt;br /&gt;
|-&lt;br /&gt;
| 0x43&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Unused???, accelometer related&lt;br /&gt;
|-&lt;br /&gt;
| 0x44&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???, pedoometer related(?)&lt;br /&gt;
|-&lt;br /&gt;
| 0x45&lt;br /&gt;
- 0x4A&lt;br /&gt;
| s&lt;br /&gt;
| ro&lt;br /&gt;
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  AXIS&lt;br /&gt;
!  V=0x00&lt;br /&gt;
!  V=0x40&lt;br /&gt;
!  V=0xC0 &lt;br /&gt;
|-&lt;br /&gt;
| X (left/right)&lt;br /&gt;
| held vertically&lt;br /&gt;
| rotated or tilted left&lt;br /&gt;
| rotated or tilted right&lt;br /&gt;
|-&lt;br /&gt;
| Y (forwards/backwards)&lt;br /&gt;
| laid flat&lt;br /&gt;
| held up&lt;br /&gt;
| helf up upside-down&lt;br /&gt;
|-&lt;br /&gt;
| Z (???)&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
| ???&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 0x4B&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| PedometerStepCount (for the current day)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4C&lt;br /&gt;
0x4D&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
| ??&lt;br /&gt;
|-&lt;br /&gt;
| 0x4E&lt;br /&gt;
| d&lt;br /&gt;
| rw&lt;br /&gt;
| ??? this = (0xFFE9E &amp;amp; 1) ? 0x10 : 0&lt;br /&gt;
|-&lt;br /&gt;
| 0x4F&lt;br /&gt;
| d(6)&lt;br /&gt;
| ro&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x51&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ???&lt;br /&gt;
|-&lt;br /&gt;
| 0x52&lt;br /&gt;
- 0x57&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it&#039;s the volume slider&#039;s maximum raw value)&lt;br /&gt;
|-&lt;br /&gt;
| 0x59&lt;br /&gt;
| s&lt;br /&gt;
| rw&lt;br /&gt;
| Register-mapped ADC register&lt;br /&gt;
DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)&lt;br /&gt;
|-&lt;br /&gt;
| 0x5A&lt;br /&gt;
| s&lt;br /&gt;
| ro/rw&lt;br /&gt;
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.&lt;br /&gt;
|-&lt;br /&gt;
| 0x5B&lt;br /&gt;
- 0x5F&lt;br /&gt;
| s&lt;br /&gt;
| - &lt;br /&gt;
| These registers are out of bounds (0xFFC00 and up), they don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| ds&lt;br /&gt;
| rw&lt;br /&gt;
| Looping queue register&lt;br /&gt;
Writing to first byte resets the queue position to the nth element&lt;br /&gt;
Reading from this register causes the values to shift up by `readsize-1`(needs confirmation) bytes after returning `readsize-1` bytes from the top of the stack (first byte is read-only, so is always zero)&lt;br /&gt;
|-&lt;br /&gt;
| 0x61&lt;br /&gt;
| ds(0x100)&lt;br /&gt;
| rw&lt;br /&gt;
| Writing to this register pushes values on top of register 0x60&#039;s stack. Reading from this register doesn&#039;t advance the stack.&lt;br /&gt;
The first byte is used to store flags for managing FIRM/NS state - bit0 = &amp;quot;WirelessDisabled&amp;quot;, bit1 = &amp;quot;SoftwareClosed&amp;quot;, bit2 = &amp;quot;PowerOffInitiated&amp;quot;, bit4 = &amp;quot;LegacyJumpProhibited&amp;quot;. This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn&#039;t seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.&lt;br /&gt;
|-&lt;br /&gt;
| 0x62 - 0x7E&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|-&lt;br /&gt;
| 0x7F&lt;br /&gt;
| d(9-0x13)&lt;br /&gt;
| ro&lt;br /&gt;
| Various system state information (debug pointer table)&lt;br /&gt;
  byte 0x06: battery related? (seems to decrease while charging and increase while discharging)&lt;br /&gt;
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)&lt;br /&gt;
  byte 0x0A: Red Power LED mode (0 = off, 1 = on)&lt;br /&gt;
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)&lt;br /&gt;
  byte 0x0D: RGB LED red intensity&lt;br /&gt;
  byte 0x0E: RGB LED green intensity&lt;br /&gt;
  byte 0x0F: RGB LED blue intensity&lt;br /&gt;
  byte 0x11: WiFi LED brightness&lt;br /&gt;
  byte 0x12: raw button states?&lt;br /&gt;
    bit0: unset while Power button is held&lt;br /&gt;
    bit1: unset while HOME button is held&lt;br /&gt;
    bit2: unset while WiFi slider is held&lt;br /&gt;
    bit5: unset while the charging LED is active&lt;br /&gt;
    bit6: unset while charger is plugged in&lt;br /&gt;
&lt;br /&gt;
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.&lt;br /&gt;
|-&lt;br /&gt;
| 0x80&lt;br /&gt;
- 0xFF&lt;br /&gt;
| s&lt;br /&gt;
| -&lt;br /&gt;
| These registers don&#039;t exist, writing is no-op, reading will yield FFs.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Shared register: the letter &amp;quot;s&amp;quot;  means that the given register is in a &amp;quot;shared register pool&amp;quot;, meaning the resgister is in the register pool in RAM at address &amp;lt;code&amp;gt;0xFFBA4 + registernumber&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dynamic register: these registers aren&#039;t in the shared pool, they just &amp;quot;pretend&amp;quot; to be there. These registers often don&#039;t retain their set value, change rapidly, or control various hardware.&lt;br /&gt;
&lt;br /&gt;
Non-shared (dynamic) register: it&#039;s a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.&lt;br /&gt;
&lt;br /&gt;
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.&lt;br /&gt;
&lt;br /&gt;
== Device 5 &amp;amp; 6 ==&lt;br /&gt;
LCD controllers for main/sub displays, most likely.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Register&lt;br /&gt;
!  Width&lt;br /&gt;
!  Name&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x11&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_IN/CMD_RESULT1&lt;br /&gt;
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.&lt;br /&gt;
|-&lt;br /&gt;
| 0x41&lt;br /&gt;
| 8&lt;br /&gt;
| CMD_RESULT0&lt;br /&gt;
| Read result &lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| 0xFE&lt;br /&gt;
| 8&lt;br /&gt;
| ?&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device 10 ==&lt;br /&gt;
See the datasheet linked to on the [[Hardware]] page for reference.&lt;br /&gt;
&lt;br /&gt;
== Device 12 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  REGISTER&lt;br /&gt;
!  WIDTH&lt;br /&gt;
!  DESCRIPTION &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 21&lt;br /&gt;
| DebugPad state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the DebugPad device, see [[HID_Shared_Memory|here]].&lt;br /&gt;
&lt;br /&gt;
== Device 13 ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Raw I2C register address&lt;br /&gt;
!  Internal register address&lt;br /&gt;
!  Width&lt;br /&gt;
!  Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x40&lt;br /&gt;
| RHR / THR (data receive/send FIFO)&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| IER&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| FCR/IIR&lt;br /&gt;
|-&lt;br /&gt;
| 0x18&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| LCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x20&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x1&lt;br /&gt;
| MCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x28&lt;br /&gt;
| 0x5&lt;br /&gt;
| 0x1&lt;br /&gt;
| LSR&lt;br /&gt;
|-&lt;br /&gt;
| 0x30&lt;br /&gt;
| 0x6&lt;br /&gt;
| 0x1&lt;br /&gt;
| MSR/TCR&lt;br /&gt;
|-&lt;br /&gt;
| 0x38&lt;br /&gt;
| 0x7&lt;br /&gt;
| 0x1&lt;br /&gt;
| SPR/TLR&lt;br /&gt;
|-&lt;br /&gt;
| 0x40&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x1&lt;br /&gt;
| TXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x48&lt;br /&gt;
| 0x9&lt;br /&gt;
| 0x1&lt;br /&gt;
| RXLVL&lt;br /&gt;
|-&lt;br /&gt;
| 0x50&lt;br /&gt;
| 0xA&lt;br /&gt;
| 0x1&lt;br /&gt;
| IODir&lt;br /&gt;
|-&lt;br /&gt;
| 0x58&lt;br /&gt;
| 0xB&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOState&lt;br /&gt;
|-&lt;br /&gt;
| 0x60&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x1&lt;br /&gt;
| IoIntEna&lt;br /&gt;
|-&lt;br /&gt;
| 0x68&lt;br /&gt;
| 0xD&lt;br /&gt;
| 0x1&lt;br /&gt;
| reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x70&lt;br /&gt;
| 0xE&lt;br /&gt;
| 0x1&lt;br /&gt;
| IOControl&lt;br /&gt;
|-&lt;br /&gt;
| 0x78&lt;br /&gt;
| 0xF&lt;br /&gt;
| 0x1&lt;br /&gt;
| EFCR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html datasheet] linked to on the [[Hardware]] page for reference. From that datasheet, for the structure of the I2C register address u8: &amp;quot;Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Device 14 ==&lt;br /&gt;
&lt;br /&gt;
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.&lt;br /&gt;
&lt;br /&gt;
The Cfg-module code which loads the [[Flash_Filesystem|CCAL]](nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.&lt;br /&gt;
&lt;br /&gt;
== Device 15 ==&lt;br /&gt;
This the New3DS [[NFC_Services|NFC]] controller &amp;quot;I2C&amp;quot; interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].&lt;br /&gt;
&lt;br /&gt;
Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.&lt;br /&gt;
&lt;br /&gt;
Command request / response structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Offset&lt;br /&gt;
!  Size&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x1&lt;br /&gt;
| Normally 0x10?&lt;br /&gt;
|-&lt;br /&gt;
| 0x1&lt;br /&gt;
| 0x1&lt;br /&gt;
| Command source / destination.&lt;br /&gt;
|-&lt;br /&gt;
| 0x2&lt;br /&gt;
| 0x1&lt;br /&gt;
| CmdID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3&lt;br /&gt;
| 0x1&lt;br /&gt;
| Payload size.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.&lt;br /&gt;
&lt;br /&gt;
For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.&lt;br /&gt;
&lt;br /&gt;
During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:&lt;br /&gt;
 000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 &lt;br /&gt;
 000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079&lt;br /&gt;
 000020: 31 42 35                                         1B5&lt;br /&gt;
&lt;br /&gt;
Or that is: &amp;quot;Dec 22 201214:53:50&amp;lt;binary&amp;gt;20791B5&amp;quot;. Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).&lt;br /&gt;
&lt;br /&gt;
=== NFC controller commands  ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  CmdRequest[1]&lt;br /&gt;
!  CmdID&lt;br /&gt;
!  Payload data for parameters&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x2E&lt;br /&gt;
| 0x2F&lt;br /&gt;
| Firmware image for this chunk, size varies.&lt;br /&gt;
| This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>MarcusD</name></author>
	</entry>
</feed>