https://www.3dbrew.org/w/api.php?action=feedcontributions&user=MarcusD&feedformat=atom3dbrew - User contributions [en]2024-03-29T13:52:14ZUser contributionsMediaWiki 1.35.8https://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22516I2C Registers2024-03-14T14:22:38Z<p>MarcusD: Added some more display info. TODO: move the part number and its decoding somewhere else</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit1: ShellState<br />
bit3: AdapterState<br />
bit4: BatteryChargeState<br />
bit5: Bottom screen backlight on<br />
bit6: Top screen backlight on<br />
bit7: LCD panel voltage on<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| 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.<br />
bit0: LCD panel voltage off<br />
bit1: LCD panel voltage on<br />
bit2: Bottom screen backlight off<br />
bit3: Bottom screen backlight on<br />
bit4: Top screen backlight off<br />
bit5: Top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| 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.<br />
<br />
Manufacturers:<br />
- 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xC - SHARP (LTPS(?) TN), new I2CLCD<br />
- 0xE - SHARP (TFT), found in 2DS only<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- LQ050B1LW10B<br />
- LQ = normal TFT<br />
- 050 = panel 5 inches diagonal<br />
- B = "other" display format<br />
- 1 = transmissive (backlight-compatible)<br />
- L = LVDS<br />
- W = *unknown coating type*<br />
- 10 = model number<br />
- B = *unknown suffix*<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- LS035T7LE38P (top screen)<br />
- LS = TFT (LTPS or SI-TFT ?)<br />
- 035 = panel 3.5 inches diagonal<br />
- T = "other 16:9" (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)<br />
- 7 = *unknown backing type*<br />
- L = LVDS<br />
- E = *unknown coating type*<br />
- 38 = model number<br />
- P = *unknow suffix*<br />
- LS030Q7DW48P (bottom screen)<br />
- LS = TFT (LTPS or SI-TFT ?)<br />
- 030 = panel 3 inches diagonal<br />
- Q = QVGA (320x240)<br />
- 7 = *unknown backing type*<br />
- D = parallel RGB (unspecified, but it's known to be RGB888 for this display)<br />
- W = *unknown coating type*<br />
- 48 = model number<br />
- P = *unknow suffix*<br />
- 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)<br />
|}<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Color curve red<br />
| rowspan=3 | <br />
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen<br />
<br />
Positive:<br />
- byte 00 (0xFF) - ???<br />
- byte 01 (0xFF) - ???<br />
- byte 02 (0x3F) - ???<br />
- byte 03 (0x3F) - ???<br />
- byte 04 (0x3F) - ???<br />
- byte 05 (0x3F) - ???<br />
- byte 06 (0x3F) - ???<br />
- byte 07 (0x3F) - ???<br />
- byte 08 (0x3F) - ???<br />
- byte 09 (0x3F) - ???<br />
<br />
Negative:<br />
- byte 10 (0xFF) - ???<br />
- byte 11 (0xFF) - ???<br />
- byte 12 (0x3F) - ???<br />
- byte 13 (0x3F) - ???<br />
- byte 14 (0x3F) - ???<br />
- byte 15 (0x3F) - ???<br />
- byte 16 (0x3F) - ???<br />
- byte 17 (0x3F) - ???<br />
- byte 18 (0x3F) - ???<br />
- byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Color curve green<br />
|-<br />
| 0x98-0xAB<br />
| Color curve blue<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown. This applies to the whole display panel.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform<br />
| 0x33<br />
| <br />
bit0 - top half horizontal flip<br />
bit1 - top half red-blue swap<br />
bit4 - bottom half horizontal flip<br />
bit5 - bottom half red-blue swap<br />
|-<br />
| 0x70-0x83<br />
| Analog curve top<br />
| rowspan=2 | <br />
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.<br />
<br />
Pair 1:<br />
byte 00 (0xFF) - ???<br />
byte 01 (0xFF) - ???<br />
byte 02 (0xFF) - ???<br />
byte 03 (0xFF) - ???<br />
byte 04 (0x3F) - ???<br />
byte 05 (0x3F) - ???<br />
byte 06 (0x3F) - ???<br />
byte 07 (0x3F) - ???<br />
byte 08 (0x3F) - ???<br />
byte 09 (0x3F) - ???<br />
<br />
Part 2:<br />
byte 10 (0xFF) - ???<br />
byte 11 (0xFF) - ???<br />
byte 12 (0xFF) - ???<br />
byte 13 (0xFF) - ???<br />
byte 14 (0x3F) - ???<br />
byte 15 (0x3F) - ???<br />
byte 16 (0x3F) - ???<br />
byte 17 (0x3F) - ???<br />
byte 18 (0x3F) - ???<br />
byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Analog curve bottom<br />
|}<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22425I2C Registers2023-10-28T18:54:11Z<p>MarcusD: finally correct this awful misinformation. also make the formatting slightly better</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit1: ShellState<br />
bit3: AdapterState<br />
bit4: BatteryChargeState<br />
bit5: Bottom screen backlight on<br />
bit6: Top screen backlight on<br />
bit7: LCD panel voltage on<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| 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.<br />
bit0: LCD panel voltage off<br />
bit1: LCD panel voltage on<br />
bit2: Bottom screen backlight off<br />
bit3: Bottom screen backlight on<br />
bit4: Top screen backlight off<br />
bit5: Top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Color curve red<br />
| rowspan=3 | <br />
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen<br />
<br />
Positive:<br />
- byte 00 (0xFF) - ???<br />
- byte 01 (0xFF) - ???<br />
- byte 02 (0x3F) - ???<br />
- byte 03 (0x3F) - ???<br />
- byte 04 (0x3F) - ???<br />
- byte 05 (0x3F) - ???<br />
- byte 06 (0x3F) - ???<br />
- byte 07 (0x3F) - ???<br />
- byte 08 (0x3F) - ???<br />
- byte 09 (0x3F) - ???<br />
<br />
Negative:<br />
- byte 10 (0xFF) - ???<br />
- byte 11 (0xFF) - ???<br />
- byte 12 (0x3F) - ???<br />
- byte 13 (0x3F) - ???<br />
- byte 14 (0x3F) - ???<br />
- byte 15 (0x3F) - ???<br />
- byte 16 (0x3F) - ???<br />
- byte 17 (0x3F) - ???<br />
- byte 18 (0x3F) - ???<br />
- byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Color curve green<br />
|-<br />
| 0x98-0xAB<br />
| Color curve blue<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown. This applies to the whole display panel.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform<br />
| 0x33<br />
| <br />
bit0 - top half horizontal flip<br />
bit1 - top half red-blue swap<br />
bit4 - bottom half horizontal flip<br />
bit5 - bottom half red-blue swap<br />
|-<br />
| 0x70-0x83<br />
| Analog curve top<br />
| rowspan=2 | <br />
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.<br />
<br />
Pair 1:<br />
byte 00 (0xFF) - ???<br />
byte 01 (0xFF) - ???<br />
byte 02 (0xFF) - ???<br />
byte 03 (0xFF) - ???<br />
byte 04 (0x3F) - ???<br />
byte 05 (0x3F) - ???<br />
byte 06 (0x3F) - ???<br />
byte 07 (0x3F) - ???<br />
byte 08 (0x3F) - ???<br />
byte 09 (0x3F) - ???<br />
<br />
Part 2:<br />
byte 10 (0xFF) - ???<br />
byte 11 (0xFF) - ???<br />
byte 12 (0xFF) - ???<br />
byte 13 (0xFF) - ???<br />
byte 14 (0x3F) - ???<br />
byte 15 (0x3F) - ???<br />
byte 16 (0x3F) - ???<br />
byte 17 (0x3F) - ???<br />
byte 18 (0x3F) - ???<br />
byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Analog curve bottom<br />
|}<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=22408GPU/External Registers2023-10-15T23:45:07Z<p>MarcusD: adjust spacing so it doesn't look janky</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused.<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)<br />
- Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HStart<br />
| Determines when the image is going to be displayed in the visible region (register 0x60).<br />
|-<br />
| 0x08<br />
| HBR<br />
| Right border start(?). Does nothing.<br />
<br />
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.<br />
| <br />
|-<br />
| 0x0C<br />
| HPF<br />
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.<br />
<br />
Unknown why, but a single dot of red is displayed before entering this mode.<br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold. Enables pushing pixels to the display.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| See [[#Framebuffer_format|framebuffer format]]<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| [[#Framebuffer_color_formats|Color format]]<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (framebuffer interleave config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, so framebuffer A is interleaved with itself)<br />
2 - AB (interleave framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| DMA size<br />
<br />
0 - 4 words (32 bytes)<br />
1 - 8 words (64 bytes)<br />
2 - 16 words (128 bytes)<br />
3 - ???<br />
<br />
FCRAM doesn't support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.<br />
|-<br />
| 31-16<br />
| Unknown<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=22407GPU/External Registers2023-10-15T23:43:21Z<p>MarcusD: progressive scan interlacing == interleaving</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused.<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)<br />
- Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HStart<br />
| Determines when the image is going to be displayed in the visible region (register 0x60).<br />
|-<br />
| 0x08<br />
| HBR<br />
| Right border start(?). Does nothing.<br />
<br />
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.<br />
| <br />
|-<br />
| 0x0C<br />
| HPF<br />
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.<br />
<br />
Unknown why, but a single dot of red is displayed before entering this mode.<br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold. Enables pushing pixels to the display.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| See [[#Framebuffer_format|framebuffer format]]<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| [[#Framebuffer_color_formats|Color format]]<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (framebuffer interleave config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, so framebuffer A is interleaved with itself)<br />
2 - AB (interleave framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| DMA size<br />
<br />
0 - 4 words (32 bytes)<br />
1 - 8 words (64 bytes)<br />
2 - 16 words (128 bytes)<br />
3 - ???<br />
<br />
FCRAM doesn't support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.<br />
|-<br />
| 31-16<br />
| Unknown<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=22406GPU/External Registers2023-10-15T22:10:25Z<p>MarcusD: oops, I used RGB565 for testing, but used RGBA8888 for calculation, burst size of 256bytes doesn't make sense for FCRAM anyway</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused.<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)<br />
- Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HStart<br />
| Determines when the image is going to be displayed in the visible region (register 0x60).<br />
|-<br />
| 0x08<br />
| HBR<br />
| Right border start(?). Does nothing.<br />
<br />
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.<br />
| <br />
|-<br />
| 0x0C<br />
| HPF<br />
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.<br />
<br />
Unknown why, but a single dot of red is displayed before entering this mode.<br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold. Enables pushing pixels to the display.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| See [[#Framebuffer_format|framebuffer format]]<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| [[#Framebuffer_color_formats|Color format]]<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| DMA size<br />
<br />
0 - 4 words (32 bytes)<br />
1 - 8 words (64 bytes)<br />
2 - 16 words (128 bytes)<br />
3 - ???<br />
<br />
FCRAM doesn't support DMA size 3, as it can only burst up to 16 words (128 bytes), and will show a black screen instead.<br />
|-<br />
| 31-16<br />
| Unknown<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22385I2C Registers2023-10-05T15:15:26Z<p>MarcusD: /* Custom registers for controller 0xE1 */</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Color curve red<br />
| rowspan=3 | <br />
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen<br />
<br />
Positive:<br />
- byte 00 (0xFF) - ???<br />
- byte 01 (0xFF) - ???<br />
- byte 02 (0x3F) - ???<br />
- byte 03 (0x3F) - ???<br />
- byte 04 (0x3F) - ???<br />
- byte 05 (0x3F) - ???<br />
- byte 06 (0x3F) - ???<br />
- byte 07 (0x3F) - ???<br />
- byte 08 (0x3F) - ???<br />
- byte 09 (0x3F) - ???<br />
<br />
Negative:<br />
- byte 10 (0xFF) - ???<br />
- byte 11 (0xFF) - ???<br />
- byte 12 (0x3F) - ???<br />
- byte 13 (0x3F) - ???<br />
- byte 14 (0x3F) - ???<br />
- byte 15 (0x3F) - ???<br />
- byte 16 (0x3F) - ???<br />
- byte 17 (0x3F) - ???<br />
- byte 18 (0x3F) - ???<br />
- byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Color curve green<br />
|-<br />
| 0x98-0xAB<br />
| Color curve blue<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown. This applies to the whole display panel.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform<br />
| 0x33<br />
| <br />
bit0 - top half horizontal flip<br />
bit1 - top half red-blue swap<br />
bit4 - bottom half horizontal flip<br />
bit5 - bottom half red-blue swap<br />
|-<br />
| 0x70-0x83<br />
| Analog curve top<br />
| rowspan=2 | <br />
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.<br />
<br />
Pair 1:<br />
byte 00 (0xFF) - ???<br />
byte 01 (0xFF) - ???<br />
byte 02 (0xFF) - ???<br />
byte 03 (0xFF) - ???<br />
byte 04 (0x3F) - ???<br />
byte 05 (0x3F) - ???<br />
byte 06 (0x3F) - ???<br />
byte 07 (0x3F) - ???<br />
byte 08 (0x3F) - ???<br />
byte 09 (0x3F) - ???<br />
<br />
Part 2:<br />
byte 10 (0xFF) - ???<br />
byte 11 (0xFF) - ???<br />
byte 12 (0xFF) - ???<br />
byte 13 (0xFF) - ???<br />
byte 14 (0x3F) - ???<br />
byte 15 (0x3F) - ???<br />
byte 16 (0x3F) - ???<br />
byte 17 (0x3F) - ???<br />
byte 18 (0x3F) - ???<br />
byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Analog curve bottom<br />
|}<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22384I2C Registers2023-10-05T14:50:10Z<p>MarcusD: /* Custom registers for controller 0xE1 */</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Color curve red<br />
| rowspan=3 | <br />
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen<br />
<br />
Positive:<br />
- byte 00 (0xFF) - ???<br />
- byte 01 (0xFF) - ???<br />
- byte 02 (0x3F) - ???<br />
- byte 03 (0x3F) - ???<br />
- byte 04 (0x3F) - ???<br />
- byte 05 (0x3F) - ???<br />
- byte 06 (0x3F) - ???<br />
- byte 07 (0x3F) - ???<br />
- byte 08 (0x3F) - ???<br />
- byte 09 (0x3F) - ???<br />
<br />
Negative:<br />
- byte 10 (0xFF) - ???<br />
- byte 11 (0xFF) - ???<br />
- byte 12 (0x3F) - ???<br />
- byte 13 (0x3F) - ???<br />
- byte 14 (0x3F) - ???<br />
- byte 15 (0x3F) - ???<br />
- byte 16 (0x3F) - ???<br />
- byte 17 (0x3F) - ???<br />
- byte 18 (0x3F) - ???<br />
- byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Color curve green<br />
|-<br />
| 0x98-0xAB<br />
| Color curve blue<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown. This applies to the whole display panel.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform<br />
| 0x33<br />
| <br />
bit0 - top half horizontal flip<br />
bit1 - top half red-blue swap<br />
bit4 - bottom half horizontal flip<br />
bit5 - bottom half red-blue swap<br />
|}<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22383I2C Registers2023-10-05T14:00:18Z<p>MarcusD: /* Custom registers for controller 0xC7 */</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Color curve red<br />
| rowspan=3 | <br />
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen<br />
<br />
Positive:<br />
- byte 00 (0xFF) - ???<br />
- byte 01 (0xFF) - ???<br />
- byte 02 (0x3F) - ???<br />
- byte 03 (0x3F) - ???<br />
- byte 04 (0x3F) - ???<br />
- byte 05 (0x3F) - ???<br />
- byte 06 (0x3F) - ???<br />
- byte 07 (0x3F) - ???<br />
- byte 08 (0x3F) - ???<br />
- byte 09 (0x3F) - ???<br />
<br />
Negative:<br />
- byte 10 (0xFF) - ???<br />
- byte 11 (0xFF) - ???<br />
- byte 12 (0x3F) - ???<br />
- byte 13 (0x3F) - ???<br />
- byte 14 (0x3F) - ???<br />
- byte 15 (0x3F) - ???<br />
- byte 16 (0x3F) - ???<br />
- byte 17 (0x3F) - ???<br />
- byte 18 (0x3F) - ???<br />
- byte 19 (0x3F) - ???<br />
|-<br />
| 0x84-0x97<br />
| Color curve green<br />
|-<br />
| 0x98-0xAB<br />
| Color curve blue<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22382I2C Registers2023-10-05T12:15:20Z<p>MarcusD: Add more factory regs to controller 0xC7</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers for unlock register 0x03:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Image control?<br />
| 0xD7<br />
| Most bits are unknown.<br />
<br />
bit0 - color invert<br />
bit1 - slight gamma increase<br />
|-<br />
| 0x11<br />
| Image transform?<br />
| 0x7F<br />
| Mostly unknown.<br />
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)<br />
bit1 - red-blue swap<br />
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)<br />
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)<br />
bit4 - Enable interlaced signal (use bit3 to swap fields)<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x70-0x83<br />
| Driving curve 1<br />
| rowspan=3 | <br />
| rowspan=3 | ???<br />
|-<br />
| 0x84-0x97<br />
| Driving curve 2<br />
|-<br />
| 0x98-0xAB<br />
| Driving curve 3<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22381I2C Registers2023-10-05T00:26:20Z<p>MarcusD: Add some more controller 0x01 register info</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert (D = ~D, or D = 255 - D)<br />
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x1D<br />
| ???<br />
| 0x0F<br />
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22380I2C Registers2023-10-04T20:19:30Z<p>MarcusD: Add more confirmed info about controller 0x01</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
<br />
bit0 - color value invert<br />
bit1 - color format remap<br />
bit2 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
<br />
bit0 - Horizontal Flip (scan from right to left)<br />
bit1 - red-blue swap<br />
bit2 - ???<br />
bit3 - ???<br />
bit4 - ???<br />
bit5 - ???<br />
bit6 - ???<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22379I2C Registers2023-10-04T20:04:39Z<p>MarcusD: Add more freshly discovered I2CLCD registers</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a second set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock a 2nd set of factory controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller.<br />
<br />
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x03<br />
| Factory key 2<br />
| <br />
| Write 0xAA here to unlock advanced IPS curve controls.<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
Factory mode registers unlocked by register 0xAF:<br />
* 0x41 - 0x4F<br />
* 0x58 - 0x5F<br />
* 0x67 - 0x6F<br />
* 0xD0 - 0xEF<br />
* unknown...<br />
<br />
Factory mode registers unlocked by register 0x03:<br />
* 0x04 - 0x0F<br />
* unknown...<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=22378I2C Registers2023-10-04T18:22:23Z<p>MarcusD: Start adding more info on I2CLCD</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: GPU off<br />
bit25: GPU on<br />
bit26: bottom backlight off<br />
bit27: bottom backlight on<br />
bit28: top backlight off<br />
bit29: top backlight on<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
These are the chip-on-glass display controllers, also known as I2CLCD.<br />
<br />
=== Shared registers ===<br />
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x01<br />
| Display enable<br />
| 0x11<br />
| Values:<br />
<br />
- 0x00 - screen off, slow burn-in<br />
- 0x01 - screen off, fast burn-in<br />
- 0x10 - screen on, color input used<br />
- 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)<br />
|-<br />
| 0x40<br />
| Read address<br />
| <br />
| Write to this register to set the read address.<br />
<br />
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.<br />
|-<br />
| 0x54<br />
| Checksum? trigger<br />
| 0x01<br />
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.<br />
|-<br />
| 0x55<br />
| ???<br />
| 0x03 (all) /<br />
0x07 (2DS)<br />
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).<br />
<br />
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.<br />
|-<br />
| 0x56<br />
| Checksum?<br />
| <br />
| Unknown. Read-writable with no effect (old3DS) / read-only (all).<br />
<br />
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's oneshot/bugged.<br />
|-<br />
| 0x60<br />
| ???<br />
| 0x01<br />
| Unknown. 0x00 is written here during init. Seems to have no effect.<br />
|-<br />
| 0x61<br />
| Register checksum<br />
| <br />
| Some - but not all - register values are combined using an unknown algorithm into this register. <br />
It's unknown which registers influence this value, as some registers which influence this value are read-only.<br />
|-<br />
| 0x62<br />
| ???<br />
| 0x01<br />
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.<br />
|-<br />
| 0xFE<br />
| ???<br />
| <br />
| Unknown, does nothing. 0xAA is written here during init.<br />
|-<br />
| 0xFF<br />
| Controller ID<br />
| <br />
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter.<br />
<br />
Known IDs:<br />
- 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL<br />
- 0xC3 - older old3DSXL<br />
- 0xE1 - 2DS<br />
- 0x10 - some select new3DS and new3DSXL with IPS screens<br />
- 0x01 - old3DS<br />
- 0x00 - unknown, gsp compares for this exact Controller ID for an alternate initialization path<br />
<br />
Manufacturers:<br />
- 0xC - SHARP (TN)<br />
- 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles<br />
- 0xE - unknown, found in 2DS only<br />
- 0x0 - unknown, found in old3DS (non-XL) only<br />
|}<br />
<br />
=== Custom registers for controller 0x00 ===<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x11<br />
| ???<br />
| <br />
| Unknown. Write 0x10 to initialize.<br />
|-<br />
| 0x50<br />
| ???<br />
| <br />
| Unknown. Write 0x01 to initialize.<br />
|}<br />
<br />
=== Custom registers for controller 0x01 ===<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x10<br />
| Interface config<br />
| 0xF7<br />
| Regonfigures the input pins and pin behavior of the controller.<br />
|-<br />
| 0x11<br />
| Image config<br />
| 0x7F<br />
| Image filters and pixel clock control.<br />
|-<br />
| 0x50<br />
| ???<br />
| 0x11<br />
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).<br />
|-<br />
| 0x53<br />
| ???<br />
| 0x73<br />
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.<br />
|}<br />
<br />
=== Custom registers for controller 0xC3 ===<br />
Basically the same as Controller ID 0xC7.<br />
<br />
=== Custom registers for controller 0xC7 ===<br />
This is the most common non-old3DS display controller. Quite overclockable.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
=== Custom registers for controller 0xE1 ===<br />
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.<br />
<br />
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0xAF<br />
| Factory key<br />
| <br />
| Write 0xAA here to unlock factory controls.<br />
|}<br />
<br />
=== Custom registers for controller 0x10 ===<br />
JDI IPS controller. It's currently unknown how to unlock factory registers.<br />
<br />
Factory mode registers:<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Name<br />
! Valid bits<br />
! Description<br />
|-<br />
| 0x70-0x7F<br />
| Driving curve 1-1<br />
| <br />
| <br />
|-<br />
| 0x80-0x8F<br />
| Driving curve 1-2<br />
| <br />
| <br />
|-<br />
| 0x90-0x9F<br />
| Driving curve 2-1<br />
| <br />
| <br />
|-<br />
| 0xA0-0xAF<br />
| Driving curve 2-2<br />
| <br />
| <br />
|-<br />
| 0xB0-0xBF<br />
| Driving curve 3-1<br />
| <br />
| <br />
|-<br />
| 0xC0-0xCF<br />
| Driving curve 3-2<br />
| <br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Hardware_calibration&diff=22304Hardware calibration2023-08-13T19:27:39Z<p>MarcusD: Update ConfigInfoBlk numbers from cfg sysmodule</p>
<hr />
<div>=File format=<br />
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.<br />
<br />
==Header==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x4<br />
| Magic "CCAL"<br />
|-<br />
| 0x4<br />
| 0x4<br />
| Version<br />
|-<br />
| 0x8<br />
| 0x4<br />
| Data size, always 0x7D0<br />
|-<br />
| 0xC<br />
| 0x1<br />
| Model version (?)<br />
|-<br />
| 0xD<br />
| 0x1<br />
| CAL revision (incremented each time the CAL file is updated)<br />
|-<br />
| 0xE<br />
| 0x2<br />
| [[#Aging masks|Bitmask of successful Aging tests]]<br />
|-<br />
| 0x10<br />
| 0x20<br />
| Signature of the data section.<br />
<br />
HMACSHA256 is used always except in the below cases where SHA256 is used:<br />
- devunits<br />
- PARTNER-DEBUGGER<br />
- PARTNER-CAPTURE<br />
- the SNAKE counterparts of the above<br />
- SNAKE-IS-DEBUGGER<br />
|-<br />
| 0x30<br />
| 0x1D0<br />
| Zerofilled, padding for the 512byte block size<br />
|}<br />
<br />
==Aging masks==<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| FCRAM<br />
|-<br />
| 1<br />
| LCD "flicker"/contrast (always successful)<br />
|-<br />
| 2<br />
| Camera<br />
|-<br />
| 3<br />
| Touch panel (always successful)<br />
|-<br />
| 4<br />
| Circle pad (analog stick)<br />
|-<br />
| 5<br />
| Codec<br />
|-<br />
| 6<br />
| Gyroscope<br />
|-<br />
| 7<br />
| RTC<br />
|-<br />
| 8<br />
| Accelerometer<br />
|-<br />
| 9<br />
| Surround<br />
|-<br />
| A<br />
| Adaptive BackLight (ABL)<br />
|-<br />
| B<br />
| 3D screen (ULCD)<br />
|-<br />
| C<br />
| Backlight PWM<br />
|-<br />
| D<br />
| Analog stick A (???)<br />
|-<br />
| E<br />
| Camera extensions<br />
|-<br />
| F<br />
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode<br />
|}<br />
<br />
==Data blocks==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]<br />
! Since version<br />
! Description<br />
|-<br />
| 0x00<br />
| 0x10<br />
| 0x00040000<br />
| <br />
| [[#Touch|Touch panel]]<br />
|-<br />
| 0x14<br />
| 0x08<br />
| ???<br />
| <br />
| [[#Circle_pad|Circle pad]]<br />
|-<br />
| 0x20<br />
| 2*<br />
| 0x00050000<br />
| <br />
| [[#Screen_flicker|Display panel contrast]]<br />
|-<br />
| 0x24<br />
| 1*<br />
| 0x00010000<br />
| <br />
| [[#RTC|RTC]]<br />
|-<br />
| 0x28<br />
| 1*<br />
| 0x00030000<br />
| <br />
| DSPRAM related<br />
|-<br />
| 0x30<br />
| 0x8A<br />
| ???<br />
| <br />
| [[#Camera_position|Camera position]]<br />
|-<br />
| 0xBC<br />
| 0x12<br />
| 0x00040002<br />
| <br />
| [[#Gyro|Gyroscope]]<br />
|-<br />
| 0xD0<br />
| 0xC<br />
| 0x00040003<br />
| <br />
| [[#Accel|Accelerometer]]<br />
|-<br />
| 0xE0<br />
| 0x134<br />
| 0x00020000<br />
| ???<br />
| [[#CDC|Codec]]<br />
|-<br />
| 0x218<br />
| 0x06<br />
| 0x00050007<br />
| 0x10<br />
| [[#PIT|Programmable Infrared Transmitter (PIT)]]<br />
|-<br />
| 0x220<br />
| 0x214<br />
| 0x00070000<br />
| 0x7<br />
| [[#3D_filters|3D filters]]<br />
|-<br />
| 0x440<br />
| 0x20<br />
| 0x00050003<br />
| 0x8<br />
| [[#ABL|Adaptive BackLight / Power saving mode]]<br />
|-<br />
| 0x470<br />
| 0x20<br />
| 0x00050005<br />
| 0xA<br />
| ???<br />
|-<br />
| 0x4A0<br />
| 0x38<br />
| 0x00050002<br />
| 0xB<br />
| [[#BLPWM|Backlight PWM]]<br />
|-<br />
| 0x4E0<br />
| 0x18<br />
| ???<br />
| <br />
| [[#Circle_pad_extra|Circle pad extra]]<br />
|-<br />
| 0x500<br />
| 0xC<br />
| ???<br />
| <br />
| ???<br />
|-<br />
| 0x510<br />
| 0x20<br />
| 0x00050004<br />
| 0x8<br />
| ???<br />
|-<br />
| 0x540<br />
| 0x08<br />
| 0x00120000<br />
| 0x7<br />
| [[#MCU|MCU]]<br />
|-<br />
| 0x550<br />
| 0x02<br />
| 0x00050006<br />
| 0x9<br />
| [[#ULCD_delay|3D screen (ULCD) delay]]<br />
|-<br />
| 0x560<br />
| 0x08<br />
| 0x00070002<br />
| 0xD<br />
| [[#Microphone_echo_cancel|Microphone echo cancellation]]<br />
|-<br />
| 0x570<br />
| 0x10C<br />
| 0x00050008<br />
| 0xF<br />
| [[#ABL_extra|Power saving mode (ABL) extra]]<br />
|-<br />
| 0x680<br />
| 0x08<br />
| 0x00040004<br />
| 0xF<br />
| [[#CStick|CStick (Right stick)]]<br />
|-<br />
| 0x690<br />
| 0x18<br />
| 0x00180001<br />
| 0x12<br />
| [[#QTM|Quad Tracking Module (QTM)]]<br />
|}<br />
<br />
=Data block formats=<br />
<br />
==Touch==<br />
Used for mapping touch ADC values to display pixel co-ordinates.<br />
<br />
<code><br />
[4096, 4096] --> [320, 240]<br />
<br />
[RawX, RawY] --> [PointX, PointY]<br />
</code><br />
<br />
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.<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 RawX0<br />
|-<br />
| 0x02<br />
| s16 RawY0<br />
|-<br />
| 0x04<br />
| s16 PointX0<br />
|-<br />
| 0x06<br />
| s16 PointY0<br />
|-<br />
| 0x08<br />
| s16 RawX1<br />
|-<br />
| 0x0A<br />
| s16 RawY1<br />
|-<br />
| 0x0C<br />
| s16 PointX1<br />
|-<br />
| 0x0E<br />
| s16 PointY1<br />
|}<br />
<br />
==Circle pad==<br />
<br />
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 CenterX<br />
|rowspan="2"| Raw analog values corresponding to zero input position<br />
|-<br />
| 0x02<br />
| s16 CenterY<br />
|}<br />
<br />
==Screen flicker==<br />
<br />
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 FlickerTop<br />
|rowspan="2"| Contrast voltage<br />
|-<br />
| 1<br />
| u8 FlickerBottom<br />
|-<br />
| 2*<br />
|rowspan="2"| Inline checksum<br />
| Checksum low byte, NOT THIS[0]<br />
|-<br />
| 3*<br />
| Checksum high byte, THIS[1]<br />
|}<br />
<br />
==RTC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 CompensationValue<br />
| (???)<br />
|-<br />
| 1*<br />
| <br />
| Checksum byte, NOT THIS[0]<br />
|}<br />
<br />
==Camera position==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 flags<br />
|-<br />
| 0x04<br />
| float scale<br />
|-<br />
| 0x08<br />
| float RotationZ<br />
|-<br />
| 0x0C<br />
| float TranslationX<br />
|-<br />
| 0x10<br />
| float TranslationY<br />
|-<br />
| 0x14<br />
| float RotationX<br />
|-<br />
| 0x18<br />
| float RotationY<br />
|-<br />
| 0x1C<br />
| float ViewAngleRight<br />
|-<br />
| 0x20<br />
| float ViewAngleLeft<br />
|-<br />
| 0x24<br />
| float ChartDistance(???)<br />
|-<br />
| 0x28<br />
| float CameraDistance<br />
|-<br />
| 0x2C<br />
| s16 ImageWidth<br />
|-<br />
| 0x2E<br />
| s16 ImageHeight<br />
|-<br />
| 0x30<br />
| u8 reserved[0x10]<br />
|-<br />
| 0x40<br />
| u8 ???[0x40]<br />
|-<br />
| 0x80<br />
| s16 aeBaseTarget(???)<br />
|-<br />
| 0x82<br />
| s16 kRL<br />
|-<br />
| 0x84<br />
| s16 kGL<br />
|-<br />
| 0x86<br />
| s16 kBL<br />
|-<br />
| 0x88<br />
| s16 ccmPosition<br />
|}<br />
<br />
==Gyro==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 ZeroX<br />
|-<br />
| 0x02<br />
| s16 PlusX<br />
|-<br />
| 0x04<br />
| s16 MinusX<br />
|-<br />
| 0x06<br />
| s16 ZeroY<br />
|-<br />
| 0x08<br />
| s16 PlusY<br />
|-<br />
| 0x0A<br />
| s16 MinusY<br />
|-<br />
| 0x0C<br />
| s16 ZeroZ<br />
|-<br />
| 0x0E<br />
| s16 PlusZ<br />
|-<br />
| 0x10<br />
| s16 MinusZ<br />
|}<br />
<br />
==Accel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 OffsetX<br />
|-<br />
| 0x02<br />
| s16 ScaleX<br />
|-<br />
| 0x04<br />
| s16 OffsetY<br />
|-<br />
| 0x06<br />
| s16 ScaleY<br />
|-<br />
| 0x08<br />
| s16 OffsetZ<br />
|-<br />
| 0x0A<br />
| s16 ScaleZ<br />
|}<br />
<br />
==CDC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| u8 DriverGainHP<br />
| Headphone gain<br />
|-<br />
| 0x01<br />
| u8 DriverGainSP<br />
| Speaker gain<br />
|-<br />
| 0x02<br />
| u8 AnalogVolumeHP<br />
| <br />
|-<br />
| 0x03<br />
| u8 AnalogVolumeSP<br />
| <br />
|-<br />
| 0x04<br />
| s8 ShutterVolume[2]<br />
| <br />
|-<br />
| 0x06<br />
| u8 MicrophoneBias<br />
| Capacitive microphone bias voltage<br />
|-<br />
| 0x07<br />
| u8 QuickCharge<br />
| (???)<br />
|-<br />
| 0x08<br />
| u8 PGA_GAIN<br />
| ??? (microphone gain)<br />
|-<br />
| 0x09<br />
| u8 reserved[3]<br />
|-<br />
| 0x0C<br />
| s16 FilterHP32[3*5]<br />
| Headphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x2A<br />
| s16 FilterHP47[3*5]<br />
| Headphone filter for 47605Hz sampling rate<br />
|-<br />
| 0x48<br />
| s16 FilterSP32[3*5]<br />
| Speaker filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x66<br />
| s16 FilterSP47[3*5]<br />
| Speaker filter for 47605Hz sampling rate<br />
|-<br />
| 0x84<br />
| s16 FilterMic32[(1+2)+((1+4)*5)]<br />
| Microphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0xBC<br />
| s16 FilterMic47[(1+2)+((1+4)*5)]<br />
| Microphone filter for 47605Hz sampling rate<br />
|-<br />
| 0xF4<br />
| s16 FilterFree[(1+2)+((1+4)*5)]<br />
| Unknown<br />
|-<br />
| 0x12C<br />
| u8 AnalogInterval<br />
|-<br />
| 0x12D<br />
| u8 AnalogStabilize<br />
|-<br />
| 0x12E<br />
| u8 AnalogPrecharge<br />
|-<br />
| 0x12F<br />
| u8 AnalogSense<br />
|-<br />
| 0x130<br />
| u8 AnalogDebounce<br />
|-<br />
| 0x131<br />
| u8 Analog_XP_Pullup<br />
|-<br />
| 0x132<br />
| u8 YM_Driver<br />
| ??? (circle-pad or touch panel related?)<br />
|-<br />
| 0x133<br />
| u8 reserved<br />
|}<br />
<br />
==PIT==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 VisibleFactor<br />
|-<br />
| 0x02<br />
| u16 IRFactor<br />
|}<br />
<br />
==3D filters==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 SpecialFilter[0x100]<br />
|-<br />
| 0x200<br />
| u32 IIRSurroundFilter[5]<br />
|}<br />
<br />
==ABL==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 DitherPattern<br />
|-<br />
| 0x04<br />
| s16 StartX<br />
|-<br />
| 0x06<br />
| s16 StartY<br />
|-<br />
| 0x08<br />
| u16 SizeX<br />
|-<br />
| 0x0A<br />
| u16 SizeY<br />
|-<br />
| 0x0C<br />
| s16 GTH_Ratio<br />
|-<br />
| 0x0E<br />
| u8 DitherMode<br />
|-<br />
| 0x0F<br />
| u8 MinRS<br />
|-<br />
| 0x10<br />
| u8 MaxRS<br />
|-<br />
| 0x11<br />
| u8 MinGTH<br />
|-<br />
| 0x12<br />
| u8 MinMax (???)<br />
|-<br />
| 0x13<br />
| u8 ExMax (???)<br />
|-<br />
| 0x14<br />
| u8 inertia<br />
|-<br />
| 0x15<br />
| u8 LutListRS[9]<br />
|-<br />
| 0x1E<br />
| u8 reserved[2]<br />
|}<br />
<br />
==BLPWM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float coefficient[3][3]<br />
|-<br />
| 0x24<br />
| u8 NumLevels<br />
|-<br />
| 0x25<br />
| u8 padding<br />
|-<br />
| 0x26<br />
| u16 brightnesses[7];<br />
|-<br />
| 0x34<br />
| u16 BaseDivisor<br />
|-<br />
| 0x36<br />
| u16 MinimumBrightnessHw<br />
|}<br />
<br />
==Circle pad extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float ScaleX<br />
|-<br />
| 0x04<br />
| float ScaleY<br />
|-<br />
| 0x08<br />
| s16 MaxX<br />
|-<br />
| 0x0A<br />
| s16 MinX<br />
|-<br />
| 0x0C<br />
| s16 MaxY<br />
|-<br />
| 0x0E<br />
| s16 MinY<br />
|-<br />
| 0x10<br />
| s16 type<br />
|-<br />
| 0x12<br />
| u8 unknown_padding[6]<br />
|}<br />
<br />
==MCU==<br />
<br />
Somewhat misleading, these values are actually used for clamping the MCU's raw slider readings to comprehensible values.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 SVR2_Min<br />
| Raw 3D volume slider values <= this map to 3D slider value 0.0<br />
|-<br />
| 0x02<br />
| s16 SVR2_Max<br />
| Raw 3D volume slider values >= this map to 3D slider value 1.0<br />
|-<br />
| 0x04<br />
| s16 VolumeSliderMin<br />
| Written to MCU reg 0x58. Volume slider values <= this map to volume value 0x00<br />
|-<br />
| 0x06<br />
| s16 VolumeSliderMax<br />
| Written to MCU reg 0x59. Volume slider values >= this map to volume value 0x3F<br />
|}<br />
<br />
==ULCD delay==<br />
<br />
There is a delay between switching the parallax barrier, and adjusting the backlight.<br />
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.<br />
<br />
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.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 To2D<br />
|-<br />
| 0x01<br />
| u8 To3D<br />
|}<br />
<br />
==Microphone echo cancel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s8 params[8]<br />
|}<br />
<br />
==ABL extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 MaxInertia<br />
|-<br />
| 0x01<br />
| u8 pad<br />
|-<br />
| 0x02<br />
| u16 PWM_CNT_EX<br />
|-<br />
| 0x04<br />
| u32 Histogram1<br />
|-<br />
| 0x08<br />
| u32 Histogram2<br />
|-<br />
| 0x0C<br />
| u32 adjust[0x40]<br />
|}<br />
<br />
==CStick==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 ThinningCountX(???)<br />
|-<br />
| 0x01<br />
| u8 ThinningCountY(???)<br />
|-<br />
| 0x02<br />
| u16 reserved[3]<br />
|}<br />
<br />
==QTM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float DivisorAtZero (???)<br />
|-<br />
| 0x04<br />
| float TranslationX<br />
|-<br />
| 0x08<br />
| float TranslationY<br />
|-<br />
| 0x0C<br />
| float RotationZ<br />
|-<br />
| 0x10<br />
| float HorizontalAngle<br />
|-<br />
| 0x14<br />
| float OptimalDistance<br />
|}<br />
<br />
=Reading=<br />
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the <code>i2c:EEP</code> service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.<br />
Otherwise attempt is made to read <code>CTRNAND:/ro/sys/HWCAL(0|1).dat</code> instead.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Mysteries&diff=21914Mysteries2022-07-12T22:41:40Z<p>MarcusD: replace abbreviations with newly acquired information</p>
<hr />
<div>The following is a list of mysteries.<br />
<br />
== General ==<br />
* What is the CTR abbreviation?<br />
: C may stand for Chiheisen ("horizon" in Japanese, the O3DS's codename being "Project Horizon").<br />
:: Not true, Horizon refers to the OS.<br />
: CTR stands for Citrus.<br />
<br />
== Hardware ==<br />
=== Why are there two CTRCARD controllers? ===<br />
'''Background:''' 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)<br />
<br />
=== Why are there two EMMC controllers? ===<br />
'''Theory:''' At some point during 3DS hardware development there was an idea to split up CTR and TWL nand into two different chips.<br />
=== Is there a JTAG? ===<br />
=== Is there more than one revision of the bootrom? ===<br />
'''Background:''' 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.<br />
<br />
=== What is the EMMC controller @ 0x10100000 doing? ===<br />
'''Background:''' There's dead code in NWM referencing it.<br />
=== Why did they put NTRCARD accessible from ARM11? ===<br />
'''Theory:''' 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.<br />
<br />
=== Is there a secret message embedded in the 3DS keyscrambler constant? ===<br />
'''Background:''' TWL key scrambler constant was "Nintendo Co., Ltd" in Japanese ("任天堂株式会社"), UTF-16LE encoded, with byte order mark. The 3DS key scrambler constant, by comparison, is random-looking.<br />
<br />
=== What is the PDN abbreviation? ===<br />
: PowerDowN<br />
<br />
=== How does Nintendo reflash bricked systems? ===<br />
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.<br />
This allows to execute a FIRM that is probably used by Nintendo to reflash the system.<br />
<br />
== Software ==<br />
=== What was the problem in "initial program loader" that was mentioned in an FCC filing by Nintendo for 2DS? ===<br />
'''Background:''' http://www.neogaf.com/forum/showthread.php?t=814624&page=1<br />
=== What did SVC 0x74 in the ARM11 kernel do before it got stubbed? ===<br />
=== What is the PTM abbreviation? ===<br />
: PlayTime Management<br />
<br />
=== Why is the DTCM not used anywhere except bootrom? ===<br />
'''Background:''' Bootrom is known to use part of DTCM as state, memsetting it to 0 when it's done. After that, it is never used again.<br />
=== How is CTRAging launched during factory setup? ===<br />
'''Background:''' 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. <br />
<br />
'''Theory:''' NtrBoot another time <br />
=== Why are there 4 stubbed syscalls named SendSyncRequest1-4? ===<br />
=== Is there a deterministic formula for calculating the Movable.sed KeyY high u64? ===<br />
'''Background:''' 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's difficult to imagine this is just a coincidence.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21818GPU/External Registers2022-02-12T12:30:01Z<p>MarcusD: Update horizontal timing parameters (continuation)</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)<br />
- Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HStart<br />
| Determines when the image is going to be displayed in the visible region (register 0x60).<br />
|-<br />
| 0x08<br />
| HBR<br />
| Right border start(?). Does nothing.<br />
<br />
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.<br />
| <br />
|-<br />
| 0x0C<br />
| HPF<br />
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.<br />
<br />
Unknown why, but a single dot of red is displayed before entering this mode.<br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold. Enables pushing pixels to the display.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| See [[#Framebuffer_format|framebuffer format]]<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| [[#Framebuffer_color_formats|Color format]]<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| DMA size<br />
<br />
0 - 64 bytes<br />
1 - 128 bytes<br />
2 - 256 bytes<br />
3 - ???<br />
<br />
FCRAM doesn't support DMA size 3.<br />
|-<br />
| 31-16<br />
| Unknown<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21817GPU/External Registers2022-02-11T22:45:00Z<p>MarcusD: Clarify border register</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through xTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in no particular order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Front porch (68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
- Unknown synchronization/blanking (unknown where it happens)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HParam5<br />
| <br />
|-<br />
| 0x08<br />
| HParam7<br />
| <br />
|-<br />
| 0x0C<br />
| HParam8<br />
| <br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold. Enables pushing pixels to the display.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| Bit 0-2: framebuffer format<br />
Bit 3: null (unwritable)<br />
Bit 4-7: unknown<br />
Bit 8-9: DMA size<br />
Bit 10-15: null (unwritable)<br />
Bit 16-31: unknown<br />
<br />
DMA sizes (in bytes):<br />
0 - 64<br />
1 - 128<br />
2 - 256<br />
3 - ???<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21816GPU/External Registers2022-02-11T22:40:01Z<p>MarcusD: Add hexadecimal prefix, and clarify relation meaning</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows (values may overflow through xTotal register value):<br />
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10<br />
0x18 <= 0x60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in no particular order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Front porch (68 clock min, otherwise the screen doesn't sync properly, and really glitches out)<br />
- Unknown synchronization/blanking (unknown where it happens)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HParam5<br />
| <br />
|-<br />
| 0x08<br />
| HParam7<br />
| <br />
|-<br />
| 0x0C<br />
| HParam8<br />
| <br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16).<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| Bit 0-2: framebuffer format<br />
Bit 3: null (unwritable)<br />
Bit 4-7: unknown<br />
Bit 8-9: DMA size<br />
Bit 10-15: null (unwritable)<br />
Bit 16-31: unknown<br />
<br />
DMA sizes (in bytes):<br />
0 - 64<br />
1 - 128<br />
2 - 256<br />
3 - ???<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21815GPU/External Registers2022-02-11T22:33:01Z<p>MarcusD: Adding new PDC reg names and descriptions (continuation)</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows:<br />
10 < 14 <= 60.LO <= 04 <= 60.HI <= 08 <= 0C<br />
18 <= 60.LO<br />
<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in no particular order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed, min 16 pixel clocks, otherwise the screen gets glitchy)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Front porch (68 clock min, otherwise the screenn doesn't sync properly, and really glitches out)<br />
- Unknown synchronization/blanking (unknown where it happens)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HParam5<br />
| <br />
|-<br />
| 0x08<br />
| HParam7<br />
| <br />
|-<br />
| 0x0C<br />
| HParam8<br />
| <br />
|-<br />
| 0x10<br />
| HSync<br />
| Triggers a HSync pulse.<br />
<br />
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.<br />
|-<br />
| 0x14<br />
| HPB<br />
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.<br />
<br />
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).<br />
|-<br />
| 0x18<br />
| HBL<br />
| Left border trigger treshold.<br />
<br />
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.<br />
<br />
Can be lower than HSync, as the back porch is what takes the controller out of HSync.<br />
<br />
Must be <= HDisp start (reg 0x60 low u16).<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| HDisp<br />
| low u16: Image start (border --> pixel data)<br />
high u16: Image end (pixel data --> border)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| Bit 0-2: framebuffer format<br />
Bit 3: null (unwritable)<br />
Bit 4-7: unknown<br />
Bit 8-9: DMA size<br />
Bit 10-15: null (unwritable)<br />
Bit 16-31: unknown<br />
<br />
DMA sizes (in bytes):<br />
0 - 64<br />
1 - 128<br />
2 - 256<br />
3 - ???<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21814GPU/External Registers2022-02-11T21:26:13Z<p>MarcusD: Get rid of misleading and wrong information, and add more researched PDC info (unfinished)</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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<br />
<br />
The horizontal timing parameter order is as follows:<br />
0x10 --> 0x14 --> 0x18 --> 0x60 low --> 0x04 --> 0x60 high --> 0x08 --> 0x0C.<br />
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.<br />
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.<br />
<br />
Known latched modes (in no particular order):<br />
- HSync (triggers a line to the LCD to move to the next line)<br />
- Back porch (area between HSync and border being displayed)<br />
- Left border start (no image data is being displayed, just a configurable solid color)<br />
- Image start (pixel data is being DMA'd from video memory or main RAM)<br />
- Right border start/Image end (border color is being displayed after the main image)<br />
- Front porch (unknown where it happens)<br />
- Unknown synchronization/blanking (unknown where it happens)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| HParam5<br />
| <br />
|-<br />
| 0x08<br />
| HParam7<br />
| <br />
|-<br />
| 0x0C<br />
| HParam8<br />
| <br />
|-<br />
| 0x10<br />
| HParam1<br />
| <br />
|-<br />
| 0x14<br />
| HParam2<br />
| <br />
|-<br />
| 0x18<br />
| HParam3<br />
| <br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| low u16: ???<br />
high u16: ???<br />
| ???<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: Image width (including some offset?)<br />
high u16: Image height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: HParam4<br />
high u16: HParam6<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format and other settings<br />
| Bit 0-2: framebuffer format<br />
Bit 3: null (unwritable)<br />
Bit 4-7: unknown<br />
Bit 8-9: DMA size<br />
Bit 10-15: null (unwritable)<br />
Bit 16-31: unknown<br />
<br />
DMA sizes (in bytes):<br />
0 - 64<br />
1 - 128<br />
2 - 256<br />
3 - ???<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21624GPU/External Registers2021-11-27T20:36:15Z<p>MarcusD: Fix a missing brace in calculation and make register more clear</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (HTotal + 1)<br />
|-<br />
| 0x04<br />
| ?<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = PClock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21623GPU/External Registers2021-11-27T20:31:38Z<p>MarcusD: Start cleaning up rotated screen clutter from PDC reg descriptions and add some mroe info</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.<br />
<br />
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.<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| HTotal<br />
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.<br />
<br />
HClock = PClock / (this value + 1)<br />
|-<br />
| 0x04<br />
| ?<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| VTotal<br />
| Total height of the timing window. Can be interpreted as the vertical clock divider.<br />
<br />
VClock = Pclock / (HTotal + 1) / (VTotal + 1)<br />
<br />
Setting this to 494 lowers framerate to about 50.040660858 Hz (268111856 / 24 / (250 + 1) / (494 + 1)).<br />
|-<br />
| 0x28<br />
| ?<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| ?<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| 24bits(? top 8bits ignored)<br />
<br />
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.<br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| 32bits (bottom 3bits ignored?)<br />
<br />
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
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.<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21622GPU/External Registers2021-11-25T21:13:30Z<p>MarcusD: Add and update some newly researched PDC regs</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| H-total (V-total on not physically rotated screens).<br />
| 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x1C<br />
| H Interrupt timing<br />
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.<br />
<br />
There seems to be some limitations though:<br />
* low u16 must be smaller than high u16<br />
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal<br />
* setting low u16 to >= HTotal disables the interrupt ever firing<br />
<br />
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| V-total (H-total on not physically rotated screens).<br />
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x40<br />
| V Interrupt timing<br />
| 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.<br />
<br />
One important note is that it seems like the VSync interrupt always fires at HCount == 0, and there doesn't seem to be a register to control this behavior.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| HCount<br />
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.<br />
|-<br />
| 0x54<br />
| VCount<br />
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: HBlank IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
<br />
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.<br />
<br />
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's possible to "race the beam" to (ab)use this feature.<br />
<br />
Because of this simplicity, writing a negative value here VFlips the image (which appears as a HFlip if the 3DS is held properly)<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=ARM11_Interrupts&diff=21621ARM11 Interrupts2021-11-25T19:39:25Z<p>MarcusD: Clarify PDC interrupt use</p>
<hr />
<div>== Interrupts ==<br />
<br />
Interrupt priority is 0-0xF. A priority of 0xF means that the interrupt is disabled.<br />
<br />
= Private Interrupts =<br />
<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! IRQ<br />
! Listener<br />
! Description<br />
|-<br />
| 0<br />
| <br />
| MPCore software-interrupt. Used by Kernel11 to sync cores in crt0.<br />
|-<br />
| 0x1-0x3<br />
| <br />
| MPCore software-interrupt. Used by Boot11 and Kernel11 to kickstart Core1/2/3, and by Kernel11 to sync cores in crt0.<br />
|-<br />
| 0x4<br />
| Kernel<br />
| MPCore software-interrupt. Used to manage the performance counter. Also used by Kernel11 during crt0 to sync up.<br />
|-<br />
| 0x5<br />
| Kernel<br />
| MPCore software-interrupt. Does apparently nothing.<br />
|-<br />
| 0x6<br />
| Kernel<br />
| MPCore software-interrupt. Extensively used by [[SVC|KernelSetState]] (and contains most of the actual code of the latter).<br />
|-<br />
| 0x7<br />
| Kernel<br />
| MPCore software-interrupt. See [[KCacheMaintenanceInterruptEvent]]<br />
|-<br />
| 0x8<br />
| Kernel<br />
| MPCore software-interrupt. Used for scheduling.<br />
|-<br />
| 0x9<br />
| Kernel<br />
| 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's register storage.<br />
|-<br />
| 0xA<br />
| Kernel<br />
| TLB operations interrupt, see [[KTLBOperationsInterruptEvent]]<br />
|-<br />
| 0xB-0xE<br />
|<br />
| MPCore software-interrupt. Not configured.<br />
|-<br />
| 0xF<br />
| dmnt/debugger<br />
| MPCore software-interrupt. Used to abstract FIQ (debug). This interrupt is never sent to core2 nor core3 on N3DS.<br />
|-<br />
| 0x1D<br />
| Kernel<br />
| MPCore timer.<br />
|-<br />
| 0x1E<br />
| Kernel<br />
| 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's timer is used for everything.<br />
|}<br />
<br />
= Hardware Interrupts =<br />
<br />
There are 0x60 hardware interrupts starting at 0x20 and continuing up to 0x7F. These are not private and are accessible from any core.<br />
<br />
{| class="wikitable" border="1"<br />
! IRQ<br />
! Listener<br />
! Description<br />
|-<br />
| 0x24<br />
| ?<br />
| SPI bus 2 interrupt status update<br />
|-<br />
| 0x28<br />
| gsp, TwlBg<br />
| PSC0<br />
|-<br />
| 0x29<br />
| gsp, TwlBg<br />
| PSC1<br />
|-<br />
| 0x2A<br />
| gsp, TwlBg<br />
| PDC0 (Top screen VBlank0, HBlank0)<br />
|-<br />
| 0x2B<br />
| gsp, TwlBg<br />
| PDC1 (Bottom screen VBlank1, HBlank1)<br />
|-<br />
| 0x2C<br />
| gsp, TwlBg<br />
| PPF<br />
|-<br />
| 0x2D<br />
| gsp, TwlBg<br />
| P3D<br />
|-<br />
| 0x30-0x38<br />
| Kernel<br />
| Old CDMA Event 0..8 (9 separate IRQ lines)<br />
|-<br />
| 0x39<br />
| Kernel<br />
| Old CDMA Faulting (eg. CCR=0, or event>15)<br />
|-<br />
| 0x3A<br />
| Kernel<br />
| New CDMA Event 0..31 (shared IRQ line)<br />
|-<br />
| 0x3B<br />
| Kernel<br />
| New CDMA Faulting (eg. CCR=0)<br />
|-<br />
| 0x40<br />
| nwm<br />
| WIFI SDIO Controller @ 0x10122000<br />
|-<br />
| 0x41<br />
| nwm<br />
| WIFI SDIO Controller IRQ pin @ 0x10122000<br />
|-<br />
| 0x42<br />
| nwm_dev?<br />
| Debug WIFI SDIO Controller @ 0x10100000 ?<br />
|-<br />
| 0x43<br />
| nwm_dev?<br />
| Debug WIFI SDIO Controller @ 0x10100000 ?<br />
|-<br />
| 0x44<br />
| ?<br />
| NTRCARD (maybe?)<br />
|-<br />
| 0x45<br />
| mvd (New3DS)<br />
| L2B_0 (First RGB-to-RGBA Converter)<br />
|-<br />
| 0x46<br />
| mvd (New3DS)<br />
| L2B_1 (Second RGB-to-RGBA Converter)<br />
|-<br />
| 0x48<br />
| camera<br />
| Camera Bus 0 (DSi cameras)<br />
|-<br />
| 0x49<br />
| camera<br />
| Camera Bus 1 (left-eye)<br />
|-<br />
| 0x4A<br />
| dsp<br />
| General interrupt from DSP, including semaphore and command/reply registers status change<br />
|-<br />
| 0x4B<br />
| camera<br />
| Y2R Conversion Finished<br />
|-<br />
| 0x4C<br />
| TwlBg<br />
| LGYFB_0 Legacy GBA/NDS Video<br />
|-<br />
| 0x4D<br />
| TwlBg<br />
| LGYFB_1 Legacy GBA/NDS Video<br />
|-<br />
| 0x4E<br />
| mvd (New3DS)<br />
| Y2R2 End Event<br />
|-<br />
| 0x4F<br />
| mvd (New3DS)<br />
| MVD general interrupt?<br />
|-<br />
| 0x50<br />
| pxi, TwlBg<br />
| Sync (bit 29 from Arm9's PXI_SYNC)<br />
|-<br />
| 0x51<br />
| pxi, TwlBg<br />
| Sync 2 (bit 30 from Arm9's PXI_SYNC)<br />
|-<br />
| 0x52<br />
| pxi, TwlBg<br />
| Send Fifo Empty<br />
|-<br />
| 0x53<br />
| pxi, TwlBg<br />
| Receive Fifo Not Empty<br />
|-<br />
| 0x54<br />
| i2c, TwlBg<br />
| I2C Bus0 work done<br />
|-<br />
| 0x55<br />
| i2c, TwlBg<br />
| I2C Bus1 work done<br />
|-<br />
| 0x56<br />
| spi, TwlBg<br />
| SPI bus 3 interrupt status update<br />
|-<br />
| 0x57<br />
| spi, TwlBg<br />
| SPI bus 1 interrupt status update<br />
|-<br />
| 0x58<br />
| Kernel<br />
| PDN (wake event or SoC mode changed)<br />
|-<br />
| 0x59<br />
| TwlBg<br />
| PDN Legacy Sleep<br />
|-<br />
| 0x5A<br />
| mic<br />
| General microphone interrupt (?)<br />
|-<br />
| 0x5B<br />
| -<br />
| [[HID_Registers#HID_PAD_CNT|HID_PAD_CNT]]<br />
|-<br />
| 0x5C<br />
| i2c, TwlBg<br />
| I2C Bus2 work done<br />
|-<br />
| 0x5F<br />
| mp<br />
| DS WiFi registers<br />
|-<br />
| 0x60<br />
| gpio, TwlBg<br />
| Shell opened (GPIO1_2 falling edge)<br />
|-<br />
| 0x62<br />
| gpio, TwlBg<br />
| Shell closed (GPIO1_2 rising edge)<br />
|-<br />
| 0x63<br />
| gpio, TwlBg<br />
| Touch Screen pressed (GPIO1_1 falling edge)<br />
|-<br />
| 0x64<br />
| gpio, TwlBg<br />
| Headphones inserted (GPIO2_0)<br />
|-<br />
| 0x66<br />
| gpio, TwlBg<br />
| TWL depop circuit (GPIO2_1)<br />
|-<br />
| 0x68<br />
| gpio, TwlBg<br />
| C-stick interrupt (GPIO3_0)<br />
|-<br />
| 0x69<br />
| gpio, TwlBg<br />
| IrDA interrupt (active-low) (GPIO3_1)<br />
|-<br />
| 0x6A<br />
| gpio, TwlBg<br />
| Gyro interrupt (GPIO3_2)<br />
|-<br />
| 0x6B<br />
| gpio, TwlBg<br />
| C-stick "stop" (output) (GPIO3_3)<br />
|-<br />
| 0x6C<br />
| gpio, TwlBg<br />
| IrDA TX-RC (output) (GPIO3_4)<br />
|-<br />
| 0x6D<br />
| gpio, TwlBg<br />
| IrDA RXD (GPIO3_5)<br />
|-<br />
| 0x6E<br />
| gpio, TwlBg<br />
| NFC output1 (?) (GPIO3_6)<br />
|-<br />
| 0x6F<br />
| gpio, TwlBg<br />
| NFC output2 (?) (GPIO3_7)<br />
|-<br />
| 0x70<br />
| gpio, TwlBg<br />
| Headphones button/half-inserted (active-low) (GPIO3_8)<br />
|-<br />
| 0x71<br />
| gpio, TwlBg<br />
| MCU interrupt (GPIO3_9)<br />
|-<br />
| 0x72<br />
| gpio, TwlBg<br />
| NFC interrupt (?) (GPIO3_10)<br />
|-<br />
| 0x73<br />
| TwlBg<br />
| QTM output (?) (GPIO3_11)<br />
|-<br />
| 0x74<br />
| ?<br />
| Gamecard related<br />
|-<br />
| 0x75<br />
| ?<br />
| Gamecard inserted<br />
|-<br />
| 0x76<br />
| -<br />
| L2C<br />
|-<br />
| 0x78 to 0x7B<br />
| Kernel<br />
| Core 0-3 Performance monitor counter (any) overflow<br />
|-<br />
| 0x7A to 0x82 (PDN_MPCORE_CFG bit2 set) or<br />
0x7C to 0x84 (bit2 clear)<br />
| Kernel<br />
| Other PMU interrupts (line may not exist at all)<br />
|}<br />
(interrupts from 0x80 and up can't be mapped in available builds of the kernel)<br />
<br />
<br />
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).<br />
<br />
The Arm11 kernel configures interrupts the following way (it seems the GPIO IRQ layout doesn't match released 3DS models):<br />
<br />
<nowiki>Interrupts 0x00 to 0x1F: edge-triggered, N-N<br />
Interrupt 0x20: level-sensitive, 1-N<br />
Interrupt 0x21: level-sensitive, 1-N<br />
Interrupt 0x22: level-sensitive, 1-N<br />
Interrupt 0x23: level-sensitive, 1-N<br />
Interrupt 0x24: edge-triggered, 1-N<br />
Interrupt 0x25: level-sensitive, 1-N<br />
Interrupt 0x28: level-sensitive, 1-N<br />
Interrupt 0x29: level-sensitive, 1-N<br />
Interrupt 0x2a: level-sensitive, 1-N<br />
Interrupt 0x2b: level-sensitive, 1-N<br />
Interrupt 0x2c: level-sensitive, 1-N<br />
Interrupt 0x2d: level-sensitive, 1-N<br />
Interrupt 0x30: level-sensitive, 1-N<br />
Interrupt 0x31: level-sensitive, 1-N<br />
Interrupt 0x32: level-sensitive, 1-N<br />
Interrupt 0x33: level-sensitive, 1-N<br />
Interrupt 0x34: level-sensitive, 1-N<br />
Interrupt 0x35: level-sensitive, 1-N<br />
Interrupt 0x36: level-sensitive, 1-N<br />
Interrupt 0x37: level-sensitive, 1-N<br />
Interrupt 0x38: level-sensitive, 1-N<br />
Interrupt 0x39: level-sensitive, 1-N<br />
Interrupt 0x3a: level-sensitive, 1-N<br />
Interrupt 0x3b: level-sensitive, 1-N<br />
Interrupt 0x40: edge-triggered, 1-N<br />
Interrupt 0x41: edge-triggered, 1-N<br />
Interrupt 0x42: edge-triggered, 1-N<br />
Interrupt 0x43: edge-triggered, 1-N<br />
Interrupt 0x44: edge-triggered, 1-N<br />
Interrupt 0x45: edge-triggered, 1-N<br />
Interrupt 0x46: edge-triggered, 1-N<br />
Interrupt 0x48: edge-triggered, 1-N<br />
Interrupt 0x49: edge-triggered, 1-N<br />
Interrupt 0x4a: edge-triggered, 1-N<br />
Interrupt 0x4b: edge-triggered, 1-N<br />
Interrupt 0x4c: edge-triggered, 1-N<br />
Interrupt 0x4d: edge-triggered, 1-N<br />
Interrupt 0x4e: edge-triggered, 1-N<br />
Interrupt 0x4f: level-sensitive, 1-N<br />
Interrupt 0x50: edge-triggered, 1-N<br />
Interrupt 0x51: edge-triggered, 1-N<br />
Interrupt 0x52: edge-triggered, 1-N<br />
Interrupt 0x53: edge-triggered, 1-N<br />
Interrupt 0x54: edge-triggered, 1-N<br />
Interrupt 0x55: edge-triggered, 1-N<br />
Interrupt 0x56: edge-triggered, 1-N<br />
Interrupt 0x57: edge-triggered, 1-N<br />
Interrupt 0x58: level-sensitive, 1-N<br />
Interrupt 0x59: edge-triggered, 1-N<br />
Interrupt 0x5a: edge-triggered, 1-N<br />
Interrupt 0x5b: edge-triggered, 1-N<br />
Interrupt 0x5f: edge-triggered, 1-N<br />
Interrupt 0x60: edge-triggered, 1-N<br />
Interrupt 0x61: edge-triggered, 1-N<br />
Interrupt 0x64: edge-triggered, 1-N<br />
Interrupt 0x65: edge-triggered, 1-N<br />
Interrupt 0x66: edge-triggered, 1-N<br />
Interrupt 0x68: edge-triggered, 1-N<br />
Interrupt 0x69: edge-triggered, 1-N<br />
Interrupt 0x6a: edge-triggered, 1-N<br />
Interrupt 0x6b: edge-triggered, 1-N<br />
Interrupt 0x6c: edge-triggered, 1-N<br />
Interrupt 0x6d: edge-triggered, 1-N<br />
Interrupt 0x6e: edge-triggered, 1-N<br />
Interrupt 0x6f: edge-triggered, 1-N<br />
Interrupt 0x70: edge-triggered, 1-N<br />
Interrupt 0x71: edge-triggered, 1-N<br />
Interrupt 0x72: edge-triggered, 1-N<br />
Interrupt 0x73: edge-triggered, 1-N<br />
Interrupt 0x74: edge-triggered, 1-N<br />
Interrupt 0x75: edge-triggered, 1-N<br />
Interrupt 0x76: level-sensitive, 1-N<br />
Interrupt 0x77: level-sensitive, 1-N<br />
Interrupt 0x78: edge-triggered, 1-N<br />
Interrupt 0x79: level-sensitive, 1-N<br />
Interrupt 0x7a: level-sensitive, 1-N<br />
Interrupt 0x7b: level-sensitive, 1-N<br />
Interrupt 0x7c: level-sensitive, 1-N<br />
Interrupt 0x7d: level-sensitive, 1-N</nowiki><br />
<br />
= InterruptData =<br />
{| class="wikitable" border="1"<br />
|-<br />
! Offset<br />
! Type<br />
! Description<br />
|-<br />
| 0x0<br />
| [[KBaseInterruptEvent]] *<br />
| Pointer to the KBaseInterruptEvent object for this interrupt <br />
|-<br />
| 0x4<br />
| u8<br />
| Interrupt will be disabled by the IRQ handler as soon as it is acknowledged.<br />
Ignored for FIQ: the FIQ handler always sets bit2 of [[PDN_Registers#PDN_FIQ_CNT|PDN_FIQ_CNT]]<br />
|-<br />
| 0x5<br />
| u8<br />
| Interrupt is disabled<br />
|-<br />
| 0x6<br />
| u8<br />
| Interrupt priority<br />
|-<br />
| 0x7<br />
| u8<br />
| Unused, alignment<br />
|}<br />
<br />
= Interrupt Table (New3DS) =<br />
(0xFFF318F4 in 10.3)<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Type<br />
! Description<br />
|-<br />
| 0x0<br />
| InterruptData[224]<br />
| Data for all hardware and software interrupts<br />
|-<br />
| 0x700<br />
| [[KObjectMutex]]<br />
| Mutex<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Pinouts&diff=21593Pinouts2021-09-24T05:35:22Z<p>MarcusD: [WIP] Update MCU info + reformat table + merge TP numbers from page history</p>
<hr />
<div>== CTR CPU B ==<br />
<br />
{| class="wikitable" style="font-family:Monospace;text-align:center;width:100%;table-layout:fixed;"<br />
| style="background: #bbbbbb" | G || style="background: #cc9900" | 0? || style="background: #336600" | CS1 || style="background: #336600" | ? || style="background: #336600" | ? || style="background: #a060a0" | D5 || style="background: #a060a0" | D2 || || style="background: #a060a0" | RST || style="background: #a060a0" | CLK || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #ff0000" | X || style="background: #ff0000" | X || style="background: #d9ffb3" | 3v3 || style="background: #d9ffb3" | 3v3 || || style="background: #d9ffb3" | 3v3 || || || || || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || || style="background: #666633" | IRIRQ || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #bbbbbb" | G<br />
|-<br />
| style="background: #cc9900" | 1? || style="background: #cc9900" | 2? || style="background: #336600" | CSx || style="background: #336600" | CSy || style="background: #336600" | ? || style="background: #a060a0" | D6 || style="background: #a060a0" | D3 || style="background: #a060a0" | D0 || style="background: #a060a0" | IRQ || style="background: #a060a0" | CS1 || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || || style="background: #d9ffb3" | 3v3 || || style="background: #bbbbbb" | G || || || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || || || || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #cc9900" | 3? || ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || ||style="background: #ffffff" | || style="background: #d9ffb3" | 3v3 || || style="background: #a060a0" | D7 || style="background: #a060a0" | D4 || style="background: #a060a0" | D1 || style="background: #a060a0" | DET || style="background: #a060a0" | CS2 || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #d9ffb3" | 3v3 || style="background: #d9ffb3" | 3v3 || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #a52a2a" | ? || style="background: #666633" | IRTX || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ffff00" | CLK || style="background: #ffff00" | D0 ||style="background: #ffffff" | || || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ffff00" | D1 || style="background: #ffff00" | D2 ||style="background: #ffffff" | || style="background: #ffff00" | D3 || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ffff00" | CMD || style="background: #ffff00" | IRQ ||style="background: #ffffff" | || style="background: #ffff00" | WP || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #00aaee" | CLK || style="background: #00aaee" | D0 ||style="background: #ffffff" | || || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #00aaee" | D1 || style="background: #00aaee" | D2 ||style="background: #ffffff" | || style="background: #00aaee" | D3 || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || style="background: #00aaee" | CMD ||style="background: #ffffff" | || || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #20b2aa" | ? || style="background: #20b2aa" | ? ||style="background: #ffffff" | || style="background: #20b2aa" | ? || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || style="background: #20b2aa" | ? ||style="background: #ffffff" | || style="background: #20b2aa" | ? || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || ||style="background: #ffffff" | || || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || ||style="background: #ffffff" | || || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #33ffff" | SCL || ||style="background: #ffffff" | || || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 3v3 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #33ffff" | SDA || ||style="background: #ffffff" | || || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 3v3 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || <br />
|-<br />
| style="background: #cc6600" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || <br />
|-<br />
| style="background: #cc6600" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #cc6600" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #cc6600" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #bbbbbb" | G || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #bbbbbb" | G ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v8 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v2 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #d9ffb3" | 1v2 ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #d9ffb3" | 1v8 || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #d9ffb3" | 1v8 || style="background: #d9ffb3" | 1v2 || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | || style="background: #cc6600" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #73e600" | ? || style="background: #476b6b" | 3? || style="background: #476b6b" | 4? || style="background: #476b6b" | 5? || || || || || style="background: #ff69b4" | B || style="background: #ff69b4" | PADR || style="background: #ff69b4" | PADD || style="background: #bbbbbb" | G || style="background: #4d4d33" | ? || style="background: #4d4d33" | ? || style="background: #bbbbbb" | G || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #ff8000" | ? || style="background: #cc6600" | ? ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | ||style="background: #ffffff" | || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #73e600" | ? || style="background: #8efab4" | SDA || style="background: #476b6b" | 1? || style="background: #476b6b" | 2? || || || || || style="background: #ff69b4" | A || style="background: #ff69b4" | STRT || style="background: #ff69b4" | PADU || style="background: #ff69b4" | L || style="background: #ff69b4" | Y || style="background: #4d4d33" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ?<br />
|-<br />
| style="background: #bbbbbb" | G || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #b19cd9" | ? || style="background: #8efab4" | SCL || style="background: #476b6b" | 0? || || || || || || || style="background: #ff69b4" | SLCT || style="background: #ff69b4" | PADL || style="background: #ff69b4" | R || style="background: #ff69b4" | X || style="background: #4d4d33" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #ff2a7f" | ? || style="background: #bbbbbb" | G<br />
|}<br />
<br />
legend:<br />
{| class="wikitable" style="font-family:Monospace;text-align:center;table-layout:fixed;"<br />
| style="background: #ff0000" | SoC clock crystal<br />
|-<br />
| style="background: #ffaaaa" | RTC clock crystal<br />
|-<br />
| style="background: #a060a0" | Gamecard<br />
|-<br />
| style="background: #ffff00" | SDCARD SDIO<br />
|-<br />
| style="background: #00aaee" | NAND SDIO<br />
|-<br />
| style="background: #20b2aa" | WIFI SDIO<br />
|-<br />
| style="background: #336600" | SPI<br />
|-<br />
| style="background: #73e600" | I2C-1<br />
|-<br />
| style="background: #8efab4" | I2C-2<br />
|-<br />
| style="background: #33ffff" | I2C-3<br />
|-<br />
| style="background: #ff69b4" | Pad<br />
|-<br />
| style="background: #ff2a7f" | FCRAM<br />
|-<br />
| style="background: #b19cd9" | Camera<br />
|-<br />
| style="background: #a52a2a" | WIFI<br />
|-<br />
| style="background: #666633" | GPIO<br />
|-<br />
| style="background: #ff8000" | LCD0 (small)<br />
|-<br />
| style="background: #cc6600" | LCD1 (big)<br />
|-<br />
| style="background: #cc9900" | CODEC0 (unknown)<br />
|-<br />
| style="background: #476b6b" | CODEC1 (unknown)<br />
|-<br />
| style="background: #4d4d33" | MCU (unknown)<br />
|-<br />
| style="background: #d9ffb3" | POWER<br />
|-<br />
| style="background: #bbbbbb" | Ground<br />
|}<br />
<br />
Orientation: Triangle bottom right on the PCB.<br />
<br />
== UC CTR ==<br />
<br />
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&r=1051991<br />
<br />
The functional pin mapping is almost exactly the same, except the GPIO port assignment is almost completely different.<br />
<br />
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.<br />
<br />
Orientation: Pin 1 marker in bottom left corner<br />
<br />
===Pinout===<br />
<br />
{| class="wikitable" style="font-family:Monospace;text-align:center;width:100%;table-layout:fixed;width:26%;"<br />
|-<br />
|style="background: #eaecf0" | 8<br />
| style="background: #d9ffb3" | + || style="background: #bbbbbb" | G || || || TP75 || style="background: #ffaaaa" | X || style="background: #ffaaaa" | X || style="background: #4d4d33" | ? <br />
|-<br />
|style="background: #eaecf0" | 7<br />
| style="background: #73e600" | SCL || || style="background: #bbbbbb" | G || || /RESET || style="background: #4d4d33" | ? || style="background: #4d4d33" | ? || style="background: #d9ffb3" | + <br />
|-<br />
|style="background: #eaecf0" | 6<br />
| style="background: #73e600" | SDA || || style="background: #d9ffb3" | + || TP77 || TP76 || || || style="background: #d9ffb3" | + <br />
|-<br />
|style="background: #eaecf0" | 5<br />
| style="background: #4d4d33" | ? || || TP78 || PWRLED1 || || || || CHRGLED<br />
|-<br />
|style="background: #eaecf0" | 4<br />
| || || || || || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G <br />
|-<br />
|style="background: #eaecf0" | 3<br />
| || PWRBTN || || || || || BATTTHM ||<br />
|-<br />
|style="background: #eaecf0" | 2<br />
| || || || PWRLED0 || || || HOMEBTN ||<br />
|-<br />
|style="background: #eaecf0" | 1<br />
| style="background: #d9ffb3" | + || || || || style="background: #8efab4" | SCL || style="background: #8efab4" | SDA || || style="background: #bbbbbb" | G <br />
|-<br />
!/<br />
!A<br />
!B<br />
!C<br />
!D<br />
!E<br />
!F<br />
!G<br />
!H<br />
|}<br />
<br />
===Pin assignment===<br />
<br />
und = undocumented / custom<br />
SFR = Special Function Register (SFR bank 1, range FFF00h - FFFFFh)<br />
ESR = Extended Special Function Register (SFR bank 2, range F0000h - F0806h)<br />
/ = active low (ground to enable, pull to power supply to disable)<br />
<br />
{| class="wikitable" border="1"<br />
! TP<br />
! Pin<br />
! Port<br />
! Purpose<br />
|-<br />
| TP79<br />
| A8<br />
| EVdd<br />
| Digital voltage source input (positive)<br />
|-<br />
| TP74<br />
| E7<br />
| /RESET<br />
| Resets the MCU when grounded, but is also used when reprogramming<br />
|-<br />
| TP75<br />
| E8<br />
| FLMD0<br />
| Flash mode(?) used when reprogramming with external programmer<br />
|-<br />
| TP76<br />
| E6<br />
| TOOL1<br />
| Used when using an ICE or debugger<br />
|-<br />
| TP77<br />
| D6<br />
| TOOL0<br />
| Multipurpose pin for reprogramming and debugging<br />
|-<br />
| <br />
| A7<br />
| SCL0 / P6.0<br />
| DSi-side I2C SCL<br />
|-<br />
| <br />
| A6<br />
| SDA0 / P6.1<br />
| DSi-side I2C SDA<br />
|-<br />
| <br />
| E1<br />
| SCL1 / ESR[510h].und<br />
| 3DS-side SCL<br />
|-<br />
| <br />
| F1<br />
| SDA1 / ESR[510h].und<br />
| 3DS-side SDA<br />
|-<br />
| <br />
| F7<br />
| /P0.1<br />
| SocReset_n (one of the two SoC reset signals)<br />
|-<br />
| <br />
| G7<br />
| /P0.0<br />
| SocReset_n (one of the two SoC reset signals)<br />
|-<br />
| <br />
| <br />
| /P3.0<br />
| Unknown. Probably resets something, as it's poked in a similar pattern to the SoC reset signals.<br />
|-<br />
| <br />
| <br />
| P5.0<br />
| Toggles something (poked in conjunction with reset signals)<br />
|-<br />
| <br />
| <br />
| P2.0<br />
| HOME button<br />
|-<br />
| <br />
| <br />
| P4.3<br />
| Charging LED(?)<br />
|-<br />
| <br />
| <br />
| P5.1<br />
| Charger "button"<br />
|-<br />
| <br />
| <br />
| P7.0<br />
| ???<br />
|-<br />
| <br />
| <br />
| P2.4<br />
| BatteryChargeState (?)<br />
|-<br />
| <br />
| <br />
| P7.3<br />
| Power button<br />
|-<br />
| <br />
| <br />
| P7.4<br />
| WiFi button<br />
|-<br />
| <br />
| <br />
| P7.6<br />
| External IRQ (MCU --> SoC)<br />
|}<br />
<br />
== CODEC ==<br />
{| class="wikitable" style="font-family:Monospace;text-align:center;width:100%;table-layout:fixed;width:36%;"<br />
| || style="background: #476b6b" | 4? || style="background: #d9ffb3" | 3v3 || || style="background: #cc9900" | 3? || style="background: #cc9900" | 0? || style="background: #336600" | ? || style="background: #336600" | ? || || style="background: #bbbbbb" | G ||<br />
|-<br />
| || style="background: #476b6b" | 3? || style="background: #476b6b" | 5? || style="background: #bbbbbb" | G || || style="background: #cc9900" | 1? || style="background: #336600" | CSx || style="background: #336600" | ? || || style="background: #bbbbbb" | G ||<br />
|-<br />
| style="background: #bbbbbb" | G || style="background: #476b6b" | 2? || style="background: #476b6b" | 0? || style="background: #bbbbbb" | G || || style="background: #cc9900" | 2? || style="background: #336600" | CSy || || || || SPEAKER1<br />
|-<br />
| TOUCH || TOUCH || style="background: #476b6b" | 1? || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || || SPEAKER1<br />
|-<br />
| TOUCH || TOUCH || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || ||<br />
|-<br />
| CPAD || CPAD || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || ||<br />
|-<br />
| || || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || || SPEAKER2<br />
|-<br />
| MIC || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || || SPEAKER2<br />
|-<br />
| JACK_R || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || || style="background: #bbbbbb" | G || || || || || || style="background: #bbbbbb" | G<br />
|-<br />
| style="background: #d9ffb3" | 3v3 || || || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G || || ||<br />
|-<br />
| || || style="background: #bbbbbb" | G || || || || JACK_L || || style="background: #bbbbbb" | G || style="background: #bbbbbb" | G ||<br />
|}<br />
<br />
== LCD (old3DS bottom) ==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Pin number<br />
! Name<br />
! Description<br />
|-<br />
| 01<br />
| -6V<br />
| <br />
|-<br />
| 02<br />
| 12V<br />
| <br />
|-<br />
| 03<br />
| CLK<br />
| Pixel clock<br />
|-<br />
| 04<br />
| /HBL<br />
| Horizontal blank (low while blanking)<br />
|-<br />
| 05<br />
| /VBL<br />
| Vertical blank (low while blanking)<br />
|-<br />
| 06<br />
| 2v2<br />
| Loopback of pin 07?<br />
|-<br />
| 07<br />
| 2v2<br />
| Content latch? Shorting this to ground or to pin 06 will "lock" the screen memory while still allowing the screen to refresh itself.<br />
|-<br />
| 08<br />
| GND<br />
| "chassi" ground<br />
|-<br />
| 09<br />
| HCL<br />
| Horizontal clock<br />
|-<br />
| 10<br />
| GND<br />
| "chassi" ground<br />
|-<br />
| 11<br />
| BIAS 1<br />
| Default ~ 4.5V - 4.8V; sets contrast<br />
|-<br />
| 12<br />
| BIAS 2<br />
| Usually matches BIAS 1; sets "flicker"<br />
|-<br />
| 13<br />
| ???<br />
| Might be a transistor? Shorted to ground if off, 2.36V if on.<br />
|-<br />
| 14<br />
| 6V<br />
| <br />
|-<br />
| 15<br />
| ???<br />
| Loopback of pin 14 ? Shorting this with pin 14 or ground will make the 3DS turn off with a harsh pop sound.<br />
|-<br />
| 16<br />
| ???<br />
| ???<br />
|-<br />
| 17<br />
| ???<br />
| ???<br />
|-<br />
| 18<br />
<br />
[...]<br />
<br />
25<br />
| RED 0<br />
<br />
[...]<br />
<br />
RED 7<br />
| Red pixel bits<br />
|-<br />
| 26<br />
| GND<br />
| "chassi" ground<br />
|-<br />
| 27<br />
<br />
[...]<br />
<br />
34<br />
| BLUE 7<br />
<br />
[...]<br />
<br />
BLUE 0<br />
| Blue pixel bits<br />
|-<br />
| 35<br />
| GND<br />
| "chassi" ground<br />
|-<br />
| 36<br />
<br />
[...]<br />
<br />
43<br />
| GREEN 7<br />
<br />
[...]<br />
<br />
GREEN 0<br />
| Green pixel bits<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21578I2C Registers2021-09-06T16:14:08Z<p>MarcusD: Added info on DebugPad</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
<br />
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.<br />
<br />
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.<br />
<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| For bit0 and 1 values, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)<br />
bit6: TWL MCU reg: NTR (0) vs TWL mode (1)<br />
bit7: TWL MCU reg: Uses NAND<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| Battery temperature (in Celcius?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| Battery percentage, fractional part (seems to have a resolution of around 0.1% according to tests)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]<br />
- Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)<br />
- Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)<br />
bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.<br />
- Asserts RESET2 (P0.1 = 0, PM0.1 = 0)<br />
- If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)<br />
- Resets TWL MCU i2c registers<br />
bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)<br />
- Asserts FCRAM_RESET (P3.0 = 0)<br />
bit4: signal that sleep mode is about to be entered (used by PTM)<br />
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.<br />
<br />
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.<br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].<br />
bit0: Signal TWL POWER button click<br />
bit1: Signal TWL reset<br />
bit2: Signal TWL power off<br />
bit3: Signal TWL battery low<br />
bit4: Signal TWL battery empty<br />
bit5: Signal TWL volume button click<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| d<br />
| wo<br />
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| rw<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| 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). <br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| d<br />
| wo<br />
| 2 bits<br />
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'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!<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| d<br />
| rw<br />
| Free register bank address (index) select<br />
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.<br />
<br />
byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"<br />
byte 1: Legacy LCD data<br />
bytes 2 and 3: Local Friend Code counter<br />
bytes 4 and 5: UUID clock sequence<br />
bytes 6 and 7: Unused<br />
bytes 8 to 175: Playtime data for legacy titles<br />
bytes 176 to 188: Playtime data<br />
bytes 188 to 199: Unused<br />
|-<br />
| 0x61<br />
| d(200)<br />
| rw<br />
| Free register bank, data is read from/written to here.<br />
<br />
Accessing N bytes of this register increments the selected index by N.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]<br />
byte 0x01: PMIC vendor code<br />
byte 0x02: Battery vendor code<br />
byte 0x03: MGIC version (major?)<br />
byte 0x04: MGIC version (minor?)<br />
byte 0x05: RCOMP(?)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
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].<br />
<br />
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=HID_Shared_Memory&diff=21577HID Shared Memory2021-09-06T16:03:28Z<p>MarcusD: /* Entry format */</p>
<hr />
<div>This page describes the format of the [[HID_Services|HID]] shared memory.<br />
<br />
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.<br />
<br />
size: 0x2b0 (System-version v4.4 - [[9.0.0-20]])<br />
<br />
=Offset 0x0=<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x8<br />
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.<br />
|-<br />
| 0x8<br />
| 0x8<br />
| 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.<br />
|-<br />
| 0x10<br />
| 0x4<br />
| Index in the following array which was last updated by HID module.<br />
|-<br />
| 0x1C<br />
| 0x4<br />
| Current [[PAD]] state.<br />
|-<br />
| 0x20<br />
| 0x4<br />
| Raw circle-pad info.<br />
|-<br />
| 0x28<br />
| 0x80<br />
| Array of 8 entries, where each entry(see below) is 0x10-bytes.<br />
|}<br />
<br />
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.<br />
<br />
==PAD State==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| A<br />
|-<br />
| 1<br />
| B<br />
|-<br />
| 2<br />
| Select<br />
|-<br />
| 3<br />
| Start<br />
|-<br />
| 4<br />
| Right<br />
|-<br />
| 5<br />
| Left<br />
|-<br />
| 6<br />
| Up<br />
|-<br />
| 7<br />
| Down<br />
|-<br />
| 8<br />
| R<br />
|-<br />
| 9<br />
| L<br />
|-<br />
| 10<br />
| X<br />
|-<br />
| 11<br />
| Y<br />
|-<br />
| 12<br />
| Inverted value of [[GPIO_Services|GPIO]] bit0.<br />
|-<br />
| 13<br />
| Inverted value of [[GPIO_Services|GPIO]] bit14.<br />
|-<br />
| 28<br />
| Circle pad right (X >= 41)<br />
|-<br />
| 29<br />
| Circle pad left (X <= -41)<br />
|-<br />
| 30<br />
| Circle pad up (Y >= 41)<br />
|-<br />
| 31<br />
| Circle pad down (Y <= -41)<br />
|}<br />
<br />
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).<br />
<br />
==Entry format==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x4<br />
| Current PAD state.<br />
|-<br />
| 0x4<br />
| 0x4<br />
| PAD state for buttons which were pressed(bitmasks which changed from value 0 to value 1) since the last HID update.<br />
|-<br />
| 0x8<br />
| 0x4<br />
| PAD state for buttons which were released(bitmasks which changed from value 1 to value 0) since the last HID update.<br />
|-<br />
| 0xC<br />
| 0x4<br />
| 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).<br />
|}<br />
<br />
=Offset 0xA8=<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x8<br />
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.<br />
|-<br />
| 0x8<br />
| 0x8<br />
| 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.<br />
|-<br />
| 0x10<br />
| 0x4<br />
| Index in the following array which was last updated by HID module.<br />
|-<br />
| 0x18<br />
| 0x8<br />
| Touch-screen entry, which contains the raw coordinate data prior to being converted to pixel coordinates.<br />
|-<br />
| 0x20<br />
| 0x40<br />
| 8 Touch-screen entries, containing pixel coordinates.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
==Entry format==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x2<br />
| X coordinate.<br />
|-<br />
|-<br />
| 0x2<br />
| 0x2<br />
| Y coordinate.<br />
|-<br />
|-<br />
| 0x4<br />
| 0x4<br />
| The u8 at +0 here is 0x0 when this entry doesn't contain any actual data, value 0x1 indicates that this entry contains actual data.<br />
|}<br />
<br />
=Offset 0x108=<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x8<br />
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.<br />
|-<br />
| 0x8<br />
| 0x8<br />
| 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.<br />
|-<br />
| 0x10<br />
| 0x4<br />
| Index in the following array which was last updated by HID module.<br />
|-<br />
| 0x18<br />
| 0x6<br />
| Current accelerometer state entry, contains the raw accelerometer output data.<br />
|-<br />
| 0x20<br />
| 0x30<br />
| Array containing 8 accelerometer entries. Each entry when updated contains accelerometer data converted from the above entry.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
==Entry format==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x2<br />
| s16. X?<br />
|-<br />
| 0x2<br />
| 0x2<br />
| s16. Y?<br />
|-<br />
|-<br />
| 0x4<br />
| 0x2<br />
| s16. Z?<br />
|}<br />
<br />
=Offset 0x158=<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x8<br />
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.<br />
|-<br />
| 0x8<br />
| 0x8<br />
| 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.<br />
|-<br />
| 0x10<br />
| 0x4<br />
| Index in the following array which was last updated by HID module.<br />
|-<br />
| 0x18<br />
| 0x6<br />
| Current gyroscope state entry.<br />
|-<br />
| 0x20<br />
| 0xC0<br />
| Array containing 32 gyroscrope entries.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
==Entry format==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x2<br />
| s16 X/roll(This the raw byte-swapped data from I2C, for the entry at +0x18 and the array entries).<br />
|-<br />
| 0x2<br />
| 0x2<br />
| Entry at +0x18: s16 Y/pitch. Array entries: s16 Z/yaw.<br />
|-<br />
|-<br />
| 0x4<br />
| 0x2<br />
| Entry at +0x18: s16 Z/yaw. Array entries: s16 Y/pitch, from the raw byte-swapped I2C data.<br />
|}<br />
<br />
=Offset 0x238=<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x8<br />
| [[SVC|svcGetSystemTick]] tick-count output, for when HID module updates entry index0 in the below array.<br />
|-<br />
| 0x8<br />
| 0x8<br />
| 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.<br />
|-<br />
| 0x10<br />
| 0x4<br />
| Index in the following array which was last updated by HID module.<br />
|-<br />
| 0x18<br />
| 0x60<br />
| Array containing 8 entries, see below.<br />
|}<br />
<br />
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.<br />
<br />
This doesn'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.<br />
<br />
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.<br />
<br />
==Entry format==<br />
{| class="wikitable" border="1"<br />
|-<br />
! Relative offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x2<br />
| Keys held<br />
|-<br />
| 0x2<br />
| 0x2<br />
| Keys just pressed<br />
|-<br />
| 0x4<br />
| 0x2<br />
| Keys just released<br />
|-<br />
| 0x6<br />
| 0x1<br />
| Left Stick X (range roughly -32 to +31)<br />
|-<br />
| 0x7<br />
| 0x1<br />
| Left Stick Y (range roughly -32 to +31)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| Right Stick X (range roughly -16 to +15)<br />
|-<br />
| 0x9<br />
| 0x1<br />
| Right Stick Y (range roughly -16 to +15)<br />
|-<br />
| 0xA<br />
| 0x2<br />
| Padding, not written by HID module.<br />
|}<br />
<br />
Note: there is code in hid sysmodule where holding +, -, DPAD Left, and A will recallibrate the sticks to zero.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21516GPU/External Registers2021-04-18T16:38:57Z<p>MarcusD: Add 2DS info to "scanline double"</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| H-total (V-total on not physically rotated screens).<br />
| 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| V-total (H-total on not physically rotated screens).<br />
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| Horizontal position counter<br />
| read-only<br />
|-<br />
| 0x54<br />
| Horizontal scanline (HBlank) counter<br />
| read-only<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: H(Blank?) IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable?* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
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.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Hardware_calibration&diff=21515Hardware calibration2021-04-17T02:52:57Z<p>MarcusD: added MCU slider cfgblk ID</p>
<hr />
<div>=File format=<br />
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.<br />
<br />
==Header==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x4<br />
| Magic "CCAL"<br />
|-<br />
| 0x4<br />
| 0x4<br />
| Version<br />
|-<br />
| 0x8<br />
| 0x4<br />
| Data size, always 0x7D0<br />
|-<br />
| 0xC<br />
| 0x1<br />
| Model version (?)<br />
|-<br />
| 0xD<br />
| 0x1<br />
| CAL revision (incremented each time the CAL file is updated)<br />
|-<br />
| 0xE<br />
| 0x2<br />
| [[#Aging masks|Bitmask of successful Aging tests]]<br />
|-<br />
| 0x10<br />
| 0x20<br />
| Signature of the data section.<br />
<br />
HMACSHA256 is used always except in the below cases where SHA256 is used:<br />
- devunits<br />
- PARTNER-DEBUGGER<br />
- PARTNER-CAPTURE<br />
- the SNAKE counterparts of the above<br />
- SNAKE-IS-DEBUGGER<br />
|-<br />
| 0x30<br />
| 0x1D0<br />
| Zerofilled, padding for the 512byte block size<br />
|}<br />
<br />
==Aging masks==<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| FCRAM<br />
|-<br />
| 1<br />
| LCD "flicker"/contrast (always successful)<br />
|-<br />
| 2<br />
| Camera<br />
|-<br />
| 3<br />
| Touch panel (always successful)<br />
|-<br />
| 4<br />
| Circle pad (analog stick)<br />
|-<br />
| 5<br />
| Codec<br />
|-<br />
| 6<br />
| Gyroscope<br />
|-<br />
| 7<br />
| RTC<br />
|-<br />
| 8<br />
| Accelerometer<br />
|-<br />
| 9<br />
| Surround<br />
|-<br />
| A<br />
| Adaptive BackLight (ABL)<br />
|-<br />
| B<br />
| 3D screen (ULCD)<br />
|-<br />
| C<br />
| Backlight PWM<br />
|-<br />
| D<br />
| Analog stick A (???)<br />
|-<br />
| E<br />
| Camera extensions<br />
|-<br />
| F<br />
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode<br />
|}<br />
<br />
==Data blocks==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]<br />
! Description<br />
|-<br />
| 0x00<br />
| 0x10<br />
| 00040000<br />
| [[#Touch|Touch panel]]<br />
|-<br />
| 0x14<br />
| 0x08<br />
| ???<br />
| [[#Circle_pad|Circle pad]]<br />
|-<br />
| 0x20<br />
| 2*<br />
| 00050000<br />
| [[#Screen_flicker|Display panel contrast]]<br />
|-<br />
| 0x24<br />
| 1*<br />
| ???<br />
| [[#RTC|RTC]]<br />
|-<br />
| 0x28<br />
| 1*<br />
| ???<br />
| DSPRAM related<br />
|-<br />
| 0x30<br />
| 0x8A<br />
| ???<br />
| [[#Camera_position|Camera position]]<br />
|-<br />
| 0xBC<br />
| 0x12<br />
| ???<br />
| [[#Gyro|Gyroscope]]<br />
|-<br />
| 0xD0<br />
| 0xC<br />
| ???<br />
| [[#Accel|Accelerometer]]<br />
|-<br />
| 0xE0<br />
| 0x134<br />
| ???<br />
| [[#CDC|Codec]]<br />
|-<br />
| 0x218<br />
| 0x06<br />
| ???<br />
| [[#PIT|Programmable Infrared Transmitter (PIT)]]<br />
|-<br />
| 0x220<br />
| 0x214<br />
| ???<br />
| [[#3D_filters|3D filters]]<br />
|-<br />
| 0x440<br />
| 0x20<br />
| ???<br />
| [[#ABL|Adaptive BackLight / Power saving mode]]<br />
|-<br />
| 0x470<br />
| 0x20<br />
| ???<br />
| ???<br />
|-<br />
| 0x4A0<br />
| 0x38<br />
| 0x00050002<br />
| [[#BLPWM|Backlight PWM]]<br />
|-<br />
| 0x4E0<br />
| 0x18<br />
| ???<br />
| [[#Circle_pad_extra|Circle pad extra]]<br />
|-<br />
| 0x500<br />
| 0xC<br />
| ???<br />
| ???<br />
|-<br />
| 0x510<br />
| 0x20<br />
| ???<br />
| ???<br />
|-<br />
| 0x540<br />
| 0x08<br />
| 0x00120000<br />
| [[#MCU|MCU]]<br />
|-<br />
| 0x550<br />
| 0x02<br />
| ???<br />
| [[#ULCD_delay|3D screen (ULCD) delay]]<br />
|-<br />
| 0x560<br />
| 0x08<br />
| ???<br />
| [[#Microphone_echo_cancel|Microphone echo cancellation]]<br />
|-<br />
| 0x570<br />
| 0x10C<br />
| ???<br />
| [[#ABL_extra|Power saving mode (ABL) extra]]<br />
|-<br />
| 0x680<br />
| 0x08<br />
| ???<br />
| [[#CStick|CStick (Right stick)]]<br />
|-<br />
| 0x690<br />
| 0x18<br />
| ???<br />
| [[#QTM|Quad Tracking Module (QTM)]]<br />
|}<br />
<br />
=Data block formats=<br />
<br />
==Touch==<br />
Used for mapping touch ADC values to display pixel co-ordinates.<br />
<br />
<code><br />
[4096, 4096] --> [320, 240]<br />
<br />
[RawX, RawY] --> [PointX, PointY]<br />
</code><br />
<br />
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.<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 RawX0<br />
|-<br />
| 0x02<br />
| s16 RawY0<br />
|-<br />
| 0x04<br />
| s16 PointX0<br />
|-<br />
| 0x06<br />
| s16 PointY0<br />
|-<br />
| 0x08<br />
| s16 RawX1<br />
|-<br />
| 0x0A<br />
| s16 RawY1<br />
|-<br />
| 0x0C<br />
| s16 PointX1<br />
|-<br />
| 0x0E<br />
| s16 PointY1<br />
|}<br />
<br />
==Circle pad==<br />
<br />
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 CenterX<br />
|rowspan="2"| Raw analog values corresponding to zero input position<br />
|-<br />
| 0x02<br />
| s16 CenterY<br />
|}<br />
<br />
==Screen flicker==<br />
<br />
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 FlickerTop<br />
|rowspan="2"| Contrast voltage<br />
|-<br />
| 1<br />
| u8 FlickerBottom<br />
|-<br />
| 2*<br />
|rowspan="2"| Inline checksum<br />
| Checksum low byte, NOT THIS[0]<br />
|-<br />
| 3*<br />
| Checksum high byte, THIS[1]<br />
|}<br />
<br />
==RTC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 CompensationValue<br />
| (???)<br />
|-<br />
| 1*<br />
| <br />
| Checksum byte, NOT THIS[0]<br />
|}<br />
<br />
==Camera position==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 flags<br />
|-<br />
| 0x04<br />
| float scale<br />
|-<br />
| 0x08<br />
| float RotationZ<br />
|-<br />
| 0x0C<br />
| float TranslationX<br />
|-<br />
| 0x10<br />
| float TranslationY<br />
|-<br />
| 0x14<br />
| float RotationX<br />
|-<br />
| 0x18<br />
| float RotationY<br />
|-<br />
| 0x1C<br />
| float ViewAngleRight<br />
|-<br />
| 0x20<br />
| float ViewAngleLeft<br />
|-<br />
| 0x24<br />
| float ChartDistance(???)<br />
|-<br />
| 0x28<br />
| float CameraDistance<br />
|-<br />
| 0x2C<br />
| s16 ImageWidth<br />
|-<br />
| 0x2E<br />
| s16 ImageHeight<br />
|-<br />
| 0x30<br />
| u8 reserved[0x10]<br />
|-<br />
| 0x40<br />
| u8 ???[0x40]<br />
|-<br />
| 0x80<br />
| s16 aeBaseTarget(???)<br />
|-<br />
| 0x82<br />
| s16 kRL<br />
|-<br />
| 0x84<br />
| s16 kGL<br />
|-<br />
| 0x86<br />
| s16 kBL<br />
|-<br />
| 0x88<br />
| s16 ccmPosition<br />
|}<br />
<br />
==Gyro==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 ZeroX<br />
|-<br />
| 0x02<br />
| s16 PlusX<br />
|-<br />
| 0x04<br />
| s16 MinusX<br />
|-<br />
| 0x06<br />
| s16 ZeroY<br />
|-<br />
| 0x08<br />
| s16 PlusY<br />
|-<br />
| 0x0A<br />
| s16 MinusY<br />
|-<br />
| 0x0C<br />
| s16 ZeroZ<br />
|-<br />
| 0x0E<br />
| s16 PlusZ<br />
|-<br />
| 0x10<br />
| s16 MinusZ<br />
|}<br />
<br />
==Accel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 OffsetX<br />
|-<br />
| 0x02<br />
| s16 ScaleX<br />
|-<br />
| 0x04<br />
| s16 OffsetY<br />
|-<br />
| 0x06<br />
| s16 ScaleY<br />
|-<br />
| 0x08<br />
| s16 OffsetZ<br />
|-<br />
| 0x0A<br />
| s16 ScaleZ<br />
|}<br />
<br />
==CDC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| u8 DriverGainHP<br />
| Headphone gain<br />
|-<br />
| 0x01<br />
| u8 DriverGainSP<br />
| Speaker gain<br />
|-<br />
| 0x02<br />
| u8 AnalogVolumeHP<br />
| <br />
|-<br />
| 0x03<br />
| u8 AnalogVolumeSP<br />
| <br />
|-<br />
| 0x04<br />
| s8 ShutterVolume[2]<br />
| <br />
|-<br />
| 0x06<br />
| u8 MicrophoneBias<br />
| Capacitive microphone bias voltage<br />
|-<br />
| 0x07<br />
| u8 QuickCharge<br />
| (???)<br />
|-<br />
| 0x08<br />
| u8 PGA_GAIN<br />
| ??? (microphone gain)<br />
|-<br />
| 0x09<br />
| u8 reserved[3]<br />
|-<br />
| 0x0C<br />
| s16 FilterHP32[3*5]<br />
| Headphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x2A<br />
| s16 FilterHP47[3*5]<br />
| Headphone filter for 47605Hz sampling rate<br />
|-<br />
| 0x48<br />
| s16 FilterSP32[3*5]<br />
| Speaker filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x66<br />
| s16 FilterSP47[3*5]<br />
| Speaker filter for 47605Hz sampling rate<br />
|-<br />
| 0x84<br />
| s16 FilterMic32[(1+2)+((1+4)*5)]<br />
| Microphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0xBC<br />
| s16 FilterMic47[(1+2)+((1+4)*5)]<br />
| Microphone filter for 47605Hz sampling rate<br />
|-<br />
| 0xF4<br />
| s16 FilterFree[(1+2)+((1+4)*5)]<br />
| Unknown<br />
|-<br />
| 0x12C<br />
| u8 AnalogInterval<br />
|-<br />
| 0x12D<br />
| u8 AnalogStabilize<br />
|-<br />
| 0x12E<br />
| u8 AnalogPrecharge<br />
|-<br />
| 0x12F<br />
| u8 AnalogSense<br />
|-<br />
| 0x130<br />
| u8 AnalogDebounce<br />
|-<br />
| 0x131<br />
| u8 Analog_XP_Pullup<br />
|-<br />
| 0x132<br />
| u8 YM_Driver<br />
| ??? (circle-pad or touch panel related?)<br />
|-<br />
| 0x133<br />
| u8 reserved<br />
|}<br />
<br />
==PIT==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 VisibleFactor<br />
|-<br />
| 0x02<br />
| u16 IRFactor<br />
|}<br />
<br />
==3D filters==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 SpecialFilter[0x100]<br />
|-<br />
| 0x200<br />
| u32 IIRSurroundFilter[5]<br />
|}<br />
<br />
==ABL==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 DitherPattern<br />
|-<br />
| 0x04<br />
| s16 StartX<br />
|-<br />
| 0x06<br />
| s16 StartY<br />
|-<br />
| 0x08<br />
| u16 SizeX<br />
|-<br />
| 0x0A<br />
| u16 SizeY<br />
|-<br />
| 0x0C<br />
| s16 GTH_Ratio<br />
|-<br />
| 0x0E<br />
| u8 DitherMode<br />
|-<br />
| 0x0F<br />
| u8 MinRS<br />
|-<br />
| 0x10<br />
| u8 MaxRS<br />
|-<br />
| 0x11<br />
| u8 MinGTH<br />
|-<br />
| 0x12<br />
| u8 MinMax (???)<br />
|-<br />
| 0x13<br />
| u8 ExMax (???)<br />
|-<br />
| 0x14<br />
| u8 inertia<br />
|-<br />
| 0x15<br />
| u8 LutListRS[9]<br />
|-<br />
| 0x1E<br />
| u8 reserved[2]<br />
|}<br />
<br />
==BLPWM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float coefficient[3][3]<br />
|-<br />
| 0x24<br />
| u8 NumLevels<br />
|-<br />
| 0x25<br />
| u8 padding<br />
|-<br />
| 0x26<br />
| u16 brightnesses[7];<br />
|-<br />
| 0x34<br />
| u16 BaseDivisor<br />
|-<br />
| 0x36<br />
| u16 MinimumBrightnessHw<br />
|}<br />
<br />
==Circle pad extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float ScaleX<br />
|-<br />
| 0x04<br />
| float ScaleY<br />
|-<br />
| 0x08<br />
| s16 MaxX<br />
|-<br />
| 0x0A<br />
| s16 MinX<br />
|-<br />
| 0x0C<br />
| s16 MaxY<br />
|-<br />
| 0x0E<br />
| s16 MinY<br />
|-<br />
| 0x10<br />
| s16 type<br />
|-<br />
| 0x12<br />
| u8 unknown_padding[6]<br />
|}<br />
<br />
==MCU==<br />
<br />
Somewhat misleading, these values are actually used for clamping the MCU's raw slider readings to comprehensible values.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 SVR2_Min<br />
| Raw 3D volume slider values <= this map to 3D slider value 0.0<br />
|-<br />
| 0x02<br />
| s16 SVR2_Max<br />
| Raw 3D volume slider values >= this map to 3D slider value 1.0<br />
|-<br />
| 0x04<br />
| s16 VolumeSliderMin<br />
| Written to MCU reg 0x58. Volume slider values <= this map to volume value 0x00<br />
|-<br />
| 0x06<br />
| s16 VolumeSliderMax<br />
| Written to MCU reg 0x59. Volume slider values >= this map to volume value 0x3F<br />
|}<br />
<br />
==ULCD delay==<br />
<br />
There is a delay between switching the parallax barrier, and adjusting the backlight.<br />
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.<br />
<br />
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.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 To2D<br />
|-<br />
| 0x01<br />
| u8 To3D<br />
|}<br />
<br />
==Microphone echo cancel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s8 params[8]<br />
|}<br />
<br />
==ABL extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 MaxInertia<br />
|-<br />
| 0x01<br />
| u8 pad<br />
|-<br />
| 0x02<br />
| u16 PWM_CNT_EX<br />
|-<br />
| 0x04<br />
| u32 Histogram1<br />
|-<br />
| 0x08<br />
| u32 Histogram2<br />
|-<br />
| 0x0C<br />
| u32 adjust[0x40]<br />
|}<br />
<br />
==CStick==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 ThinningCountX(???)<br />
|-<br />
| 0x01<br />
| u8 ThinningCountY(???)<br />
|-<br />
| 0x02<br />
| u16 reserved[3]<br />
|}<br />
<br />
==QTM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float DivisorAtZero (???)<br />
|-<br />
| 0x04<br />
| float TranslationX<br />
|-<br />
| 0x08<br />
| float TranslationY<br />
|-<br />
| 0x0C<br />
| float RotationZ<br />
|-<br />
| 0x10<br />
| float HorizontalAngle<br />
|-<br />
| 0x14<br />
| float OptimalDistance<br />
|}<br />
<br />
=Reading=<br />
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the <code>i2c:EEP</code> service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.<br />
Otherwise attempt is made to read <code>CTRNAND:/ro/sys/HWCAL(0|1).dat</code> instead.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21489GPU/External Registers2021-02-24T10:13:38Z<p>MarcusD: Corrected info about interlace control</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| H-total (V-total on not physically rotated screens).<br />
| 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| V-total (H-total on not physically rotated screens).<br />
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| Horizontal position counter<br />
| read-only<br />
|-<br />
| 0x54<br />
| Horizontal scanline (HBlank) counter<br />
| read-only<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: H(Blank?) IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 5-4<br />
| Framebuffer scanline output mode (interlace config)<br />
<br />
0 - A (output image as normal)<br />
1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)<br />
2 - AB (interlace framebuffer A and framebuffer B)<br />
3 - BA (same as above, but the line from framebuffer B is outputted first)<br />
<br />
0 is used by bottom screen at all times.<br />
1 is used by the top screen in 2D mode.<br />
2 is used by top screen in 3D mode.<br />
3 goes unused in userland.<br />
|-<br />
| 6<br />
| Scan doubling enable* (used by top screen)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
* 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's table doesn't work on the top screen, this could give a hint as to how the top screen receives the pixel data from the PDC.<br />
<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and scan doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21488GPU/External Registers2021-02-24T08:39:33Z<p>MarcusD: Document undefined color format</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| H-total (V-total on not physically rotated screens).<br />
| 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| V-total (H-total on not physically rotated screens).<br />
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| Horizontal position counter<br />
| read-only<br />
|-<br />
| 0x54<br />
| Horizontal scanline (HBlank) counter<br />
| read-only<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: H(Blank?) IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 4<br />
| Unused?<br />
|-<br />
| 5<br />
| Interlacing enable (interleaves framebuffers A and B; used by top screen in 3D mode)<br />
|-<br />
| 6<br />
| Line doubling enable (used by top screen in 2D mode)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
If only interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and line doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21487GPU/External Registers2021-02-24T07:07:23Z<p>MarcusD: Document interlacing and line doubling properly</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| VRAM bank control<br />
| Bits 8-11 = bank[i] disabled; other bits are unused<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| H-total (V-total on not physically rotated screens).<br />
| 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| V-total (H-total on not physically rotated screens).<br />
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| Horizontal position counter<br />
| read-only<br />
|-<br />
| 0x54<br />
| Horizontal scanline (HBlank) counter<br />
| read-only<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x74<br />
| PDC control<br />
| Bit 0: Enable display controller.<br />
Bit 8: H(Blank?) IRQ mask (0 = enabled).<br />
Bit 9: VBlank IRQ mask (0 = enabled).<br />
Bit 10: Error IRQ mask? (0 = enabled).<br />
Bit 16: Output enable?<br />
|-<br />
| 0x78<br />
| Framebuffer select and status<br />
| Bit 0: Next framebuffer to display (after VBlank).<br />
Bit 4: Currently displaying framebuffer?<br />
Bit 8: Reset FIFO?<br />
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.<br />
Bit 17: VBlank IRQ status/ack.<br />
Bit 18: Error IRQ status/ack?<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 4<br />
| Unused?<br />
|-<br />
| 5<br />
| Interlacing enable (interleaves framebuffers A and B; used by top screen in 3D mode)<br />
|-<br />
| 6<br />
| Line doubling enable (used by top screen in 2D mode)<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
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.<br />
<br />
<br />
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.<br />
<br />
If only interlacing is enabled, gsp detects this as a request to switch to 3D mode (gsp mode == 2), and enables the parallax barrier.<br />
It'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)<br />
<br />
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.<br />
<br />
Both interlacing and line doubling can't be enabled in usermode, but it works as expected in baremetal.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Hardware_calibration&diff=21343Hardware calibration2020-11-09T01:14:00Z<p>MarcusD: Clean up some of the non-descriptive and/or wrong variable names</p>
<hr />
<div>=File format=<br />
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.<br />
<br />
==Header==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x4<br />
| Magic "CCAL"<br />
|-<br />
| 0x4<br />
| 0x4<br />
| Version<br />
|-<br />
| 0x8<br />
| 0x4<br />
| Data size, always 0x7D0<br />
|-<br />
| 0xC<br />
| 0x1<br />
| Model version (?)<br />
|-<br />
| 0xD<br />
| 0x1<br />
| CAL revision (incremented each time the CAL file is updated)<br />
|-<br />
| 0xE<br />
| 0x2<br />
| [[#Aging masks|Bitmask of successful Aging tests]]<br />
|-<br />
| 0x10<br />
| 0x20<br />
| Signature of the data section.<br />
<br />
HMACSHA256 is used always except in the below cases where SHA256 is used:<br />
- devunits<br />
- PARTNER-DEBUGGER<br />
- PARTNER-CAPTURE<br />
- the SNAKE counterparts of the above<br />
- SNAKE-IS-DEBUGGER<br />
|-<br />
| 0x30<br />
| 0x1D0<br />
| Zerofilled, padding for the 512byte block size<br />
|}<br />
<br />
==Aging masks==<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| FCRAM<br />
|-<br />
| 1<br />
| LCD "flicker"/contrast (always successful)<br />
|-<br />
| 2<br />
| Camera<br />
|-<br />
| 3<br />
| Touch panel (always successful)<br />
|-<br />
| 4<br />
| Circle pad (analog stick)<br />
|-<br />
| 5<br />
| Codec<br />
|-<br />
| 6<br />
| Gyroscope<br />
|-<br />
| 7<br />
| RTC<br />
|-<br />
| 8<br />
| Accelerometer<br />
|-<br />
| 9<br />
| Surround<br />
|-<br />
| A<br />
| Adaptive BackLight (ABL)<br />
|-<br />
| B<br />
| 3D screen (ULCD)<br />
|-<br />
| C<br />
| Backlight PWM<br />
|-<br />
| D<br />
| Analog stick A (???)<br />
|-<br />
| E<br />
| Camera extensions<br />
|-<br />
| F<br />
| Adaptive BackLight (ABL) in legacy (DSi/GBA) mode<br />
|}<br />
<br />
==Data blocks==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! [[Config_Savegame#Configuration_blocks|ConfigInfoBlk]]<br />
! Description<br />
|-<br />
| 0x00<br />
| 0x10<br />
| 00040000<br />
| [[#Touch|Touch panel]]<br />
|-<br />
| 0x14<br />
| 0x08<br />
| ???<br />
| [[#Circle_pad|Circle pad]]<br />
|-<br />
| 0x20<br />
| 2*<br />
| 00050000<br />
| [[#Screen_flicker|Display panel contrast]]<br />
|-<br />
| 0x24<br />
| 1*<br />
| ???<br />
| [[#RTC|RTC]]<br />
|-<br />
| 0x28<br />
| 1*<br />
| ???<br />
| DSPRAM related<br />
|-<br />
| 0x30<br />
| 0x8A<br />
| ???<br />
| [[#Camera_position|Camera position]]<br />
|-<br />
| 0xBC<br />
| 0x12<br />
| ???<br />
| [[#Gyro|Gyroscope]]<br />
|-<br />
| 0xD0<br />
| 0xC<br />
| ???<br />
| [[#Accel|Accelerometer]]<br />
|-<br />
| 0xE0<br />
| 0x134<br />
| ???<br />
| [[#CDC|Codec]]<br />
|-<br />
| 0x218<br />
| 0x06<br />
| ???<br />
| [[#PIT|Programmable Infrared Transmitter (PIT)]]<br />
|-<br />
| 0x220<br />
| 0x214<br />
| ???<br />
| [[#3D_filters|3D filters]]<br />
|-<br />
| 0x440<br />
| 0x20<br />
| ???<br />
| [[#ABL|Adaptive BackLight / Power saving mode]]<br />
|-<br />
| 0x470<br />
| 0x20<br />
| ???<br />
| ???<br />
|-<br />
| 0x4A0<br />
| 0x38<br />
| 0x00050002<br />
| [[#BLPWM|Backlight PWM]]<br />
|-<br />
| 0x4E0<br />
| 0x18<br />
| ???<br />
| [[#Circle_pad_extra|Circle pad extra]]<br />
|-<br />
| 0x500<br />
| 0xC<br />
| ???<br />
| ???<br />
|-<br />
| 0x510<br />
| 0x20<br />
| ???<br />
| ???<br />
|-<br />
| 0x540<br />
| 0x08<br />
| ???<br />
| [[#MCU|MCU]]<br />
|-<br />
| 0x550<br />
| 0x02<br />
| ???<br />
| [[#ULCD_delay|3D screen (ULCD) delay]]<br />
|-<br />
| 0x560<br />
| 0x08<br />
| ???<br />
| [[#Microphone_echo_cancel|Microphone echo cancellation]]<br />
|-<br />
| 0x570<br />
| 0x10C<br />
| ???<br />
| [[#ABL_extra|Power saving mode (ABL) extra]]<br />
|-<br />
| 0x680<br />
| 0x08<br />
| ???<br />
| [[#CStick|CStick (Right stick)]]<br />
|-<br />
| 0x690<br />
| 0x18<br />
| ???<br />
| [[#QTM|Quad Tracking Module (QTM)]]<br />
|}<br />
<br />
=Data block formats=<br />
<br />
==Touch==<br />
Used for mapping touch ADC values to display pixel co-ordinates.<br />
<br />
<code><br />
[4096, 4096] --> [320, 240]<br />
<br />
[RawX, RawY] --> [PointX, PointY]<br />
</code><br />
<br />
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.<br />
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.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 RawX0<br />
|-<br />
| 0x02<br />
| s16 RawY0<br />
|-<br />
| 0x04<br />
| s16 PointX0<br />
|-<br />
| 0x06<br />
| s16 PointY0<br />
|-<br />
| 0x08<br />
| s16 RawX1<br />
|-<br />
| 0x0A<br />
| s16 RawY1<br />
|-<br />
| 0x0C<br />
| s16 PointX1<br />
|-<br />
| 0x0E<br />
| s16 PointY1<br />
|}<br />
<br />
==Circle pad==<br />
<br />
Contains the centering position of the circle pad. For other circle pad settings, see [[#Circle_pad_extra|circle pad extra]].<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 CenterX<br />
|rowspan="2"| Raw analog values corresponding to zero input position<br />
|-<br />
| 0x02<br />
| s16 CenterY<br />
|}<br />
<br />
==Screen flicker==<br />
<br />
These values are written to MCU register 0x03 and 0x04 respectively. They both set the display contrast voltage.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 FlickerTop<br />
|rowspan="2"| Contrast voltage<br />
|-<br />
| 1<br />
| u8 FlickerBottom<br />
|-<br />
| 2*<br />
|rowspan="2"| Inline checksum<br />
| Checksum low byte, NOT THIS[0]<br />
|-<br />
| 3*<br />
| Checksum high byte, THIS[1]<br />
|}<br />
<br />
==RTC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0<br />
| u8 CompensationValue<br />
| (???)<br />
|-<br />
| 1*<br />
| <br />
| Checksum byte, NOT THIS[0]<br />
|}<br />
<br />
==Camera position==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 flags<br />
|-<br />
| 0x04<br />
| float scale<br />
|-<br />
| 0x08<br />
| float RotationZ<br />
|-<br />
| 0x0C<br />
| float TranslationX<br />
|-<br />
| 0x10<br />
| float TranslationY<br />
|-<br />
| 0x14<br />
| float RotationX<br />
|-<br />
| 0x18<br />
| float RotationY<br />
|-<br />
| 0x1C<br />
| float ViewAngleRight<br />
|-<br />
| 0x20<br />
| float ViewAngleLeft<br />
|-<br />
| 0x24<br />
| float ChartDistance(???)<br />
|-<br />
| 0x28<br />
| float CameraDistance<br />
|-<br />
| 0x2C<br />
| s16 ImageWidth<br />
|-<br />
| 0x2E<br />
| s16 ImageHeight<br />
|-<br />
| 0x30<br />
| u8 reserved[0x10]<br />
|-<br />
| 0x40<br />
| u8 ???[0x40]<br />
|-<br />
| 0x80<br />
| s16 aeBaseTarget(???)<br />
|-<br />
| 0x82<br />
| s16 kRL<br />
|-<br />
| 0x84<br />
| s16 kGL<br />
|-<br />
| 0x86<br />
| s16 kBL<br />
|-<br />
| 0x88<br />
| s16 ccmPosition<br />
|}<br />
<br />
==Gyro==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 ZeroX<br />
|-<br />
| 0x02<br />
| s16 PlusX<br />
|-<br />
| 0x04<br />
| s16 MinusX<br />
|-<br />
| 0x06<br />
| s16 ZeroY<br />
|-<br />
| 0x08<br />
| s16 PlusY<br />
|-<br />
| 0x0A<br />
| s16 MinusY<br />
|-<br />
| 0x0C<br />
| s16 ZeroZ<br />
|-<br />
| 0x0E<br />
| s16 PlusZ<br />
|-<br />
| 0x10<br />
| s16 MinusZ<br />
|}<br />
<br />
==Accel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s16 OffsetX<br />
|-<br />
| 0x02<br />
| s16 ScaleX<br />
|-<br />
| 0x04<br />
| s16 OffsetY<br />
|-<br />
| 0x06<br />
| s16 ScaleY<br />
|-<br />
| 0x08<br />
| s16 OffsetZ<br />
|-<br />
| 0x0A<br />
| s16 ScaleZ<br />
|}<br />
<br />
==CDC==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| u8 DriverGainHP<br />
| Headphone gain<br />
|-<br />
| 0x01<br />
| u8 DriverGainSP<br />
| Speaker gain<br />
|-<br />
| 0x02<br />
| u8 AnalogVolumeHP<br />
| <br />
|-<br />
| 0x03<br />
| u8 AnalogVolumeSP<br />
| <br />
|-<br />
| 0x04<br />
| s8 ShutterVolume[2]<br />
| <br />
|-<br />
| 0x06<br />
| u8 MicrophoneBias<br />
| Capacitive microphone bias voltage<br />
|-<br />
| 0x07<br />
| u8 QuickCharge<br />
| (???)<br />
|-<br />
| 0x08<br />
| u8 PGA_GAIN<br />
| ??? (microphone gain)<br />
|-<br />
| 0x09<br />
| u8 reserved[3]<br />
|-<br />
| 0x0C<br />
| s16 FilterHP32[3*5]<br />
| Headphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x2A<br />
| s16 FilterHP47[3*5]<br />
| Headphone filter for 47605Hz sampling rate<br />
|-<br />
| 0x48<br />
| s16 FilterSP32[3*5]<br />
| Speaker filter for 32728.49Hz sampling rate<br />
|-<br />
| 0x66<br />
| s16 FilterSP47[3*5]<br />
| Speaker filter for 47605Hz sampling rate<br />
|-<br />
| 0x84<br />
| s16 FilterMic32[(1+2)+((1+4)*5)]<br />
| Microphone filter for 32728.49Hz sampling rate<br />
|-<br />
| 0xBC<br />
| s16 FilterMic47[(1+2)+((1+4)*5)]<br />
| Microphone filter for 47605Hz sampling rate<br />
|-<br />
| 0xF4<br />
| s16 FilterFree[(1+2)+((1+4)*5)]<br />
| Unknown<br />
|-<br />
| 0x12C<br />
| u8 AnalogInterval<br />
|-<br />
| 0x12D<br />
| u8 AnalogStabilize<br />
|-<br />
| 0x12E<br />
| u8 AnalogPrecharge<br />
|-<br />
| 0x12F<br />
| u8 AnalogSense<br />
|-<br />
| 0x130<br />
| u8 AnalogDebounce<br />
|-<br />
| 0x131<br />
| u8 Analog_XP_Pullup<br />
|-<br />
| 0x132<br />
| u8 YM_Driver<br />
| ??? (circle-pad or touch panel related?)<br />
|-<br />
| 0x133<br />
| u8 reserved<br />
|}<br />
<br />
==PIT==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 VisibleFactor<br />
|-<br />
| 0x02<br />
| u16 IRFactor<br />
|}<br />
<br />
==3D filters==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u16 SpecialFilter[0x100]<br />
|-<br />
| 0x200<br />
| u32 IIRSurroundFilter[5]<br />
|}<br />
<br />
==ABL==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u32 DitherPattern<br />
|-<br />
| 0x04<br />
| s16 StartX<br />
|-<br />
| 0x06<br />
| s16 StartY<br />
|-<br />
| 0x08<br />
| u16 SizeX<br />
|-<br />
| 0x0A<br />
| u16 SizeY<br />
|-<br />
| 0x0C<br />
| s16 GTH_Ratio<br />
|-<br />
| 0x0E<br />
| u8 DitherMode<br />
|-<br />
| 0x0F<br />
| u8 MinRS<br />
|-<br />
| 0x10<br />
| u8 MaxRS<br />
|-<br />
| 0x11<br />
| u8 MinGTH<br />
|-<br />
| 0x12<br />
| u8 MinMax (???)<br />
|-<br />
| 0x13<br />
| u8 ExMax (???)<br />
|-<br />
| 0x14<br />
| u8 inertia<br />
|-<br />
| 0x15<br />
| u8 LutListRS[9]<br />
|-<br />
| 0x1E<br />
| u8 reserved[2]<br />
|}<br />
<br />
==BLPWM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float coefficient[3][3]<br />
|-<br />
| 0x24<br />
| u8 NumLevels<br />
|-<br />
| 0x25<br />
| u8 padding<br />
|-<br />
| 0x26<br />
| u16 brightnesses[7];<br />
|-<br />
| 0x34<br />
| u16 BaseDivisor<br />
|-<br />
| 0x36<br />
| u16 MinimumBrightnessHw<br />
|}<br />
<br />
==Circle pad extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float ScaleX<br />
|-<br />
| 0x04<br />
| float ScaleY<br />
|-<br />
| 0x08<br />
| s16 MaxX<br />
|-<br />
| 0x0A<br />
| s16 MinX<br />
|-<br />
| 0x0C<br />
| s16 MaxY<br />
|-<br />
| 0x0E<br />
| s16 MinY<br />
|-<br />
| 0x10<br />
| s16 type<br />
|-<br />
| 0x12<br />
| u8 unknown_padding[6]<br />
|}<br />
<br />
==MCU==<br />
<br />
Somewhat misleading, these values are actually used for clamping the MCU's raw slider readings to comprehensible values.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
! Description<br />
|-<br />
| 0x00<br />
| s16 SVR2_Min<br />
| Raw 3D volume slider values <= this map to 3D slider value 0.0<br />
|-<br />
| 0x02<br />
| s16 SVR2_Max<br />
| Raw 3D volume slider values >= this map to 3D slider value 1.0<br />
|-<br />
| 0x04<br />
| s16 VolumeSliderMin<br />
| Written to MCU reg 0x58. Volume slider values <= this map to volume value 0x00<br />
|-<br />
| 0x06<br />
| s16 VolumeSliderMax<br />
| Written to MCU reg 0x59. Volume slider values >= this map to volume value 0x3F<br />
|}<br />
<br />
==ULCD delay==<br />
<br />
There is a delay between switching the parallax barrier, and adjusting the backlight.<br />
These delay values determine how many VBlank events to wait on before switching the backlight curves to the appropriate mode.<br />
<br />
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.<br />
<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 To2D<br />
|-<br />
| 0x01<br />
| u8 To3D<br />
|}<br />
<br />
==Microphone echo cancel==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| s8 params[8]<br />
|}<br />
<br />
==ABL extra==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 MaxInertia<br />
|-<br />
| 0x01<br />
| u8 pad<br />
|-<br />
| 0x02<br />
| u16 PWM_CNT_EX<br />
|-<br />
| 0x04<br />
| u32 Histogram1<br />
|-<br />
| 0x08<br />
| u32 Histogram2<br />
|-<br />
| 0x0C<br />
| u32 adjust[0x40]<br />
|}<br />
<br />
==CStick==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| u8 ThinningCountX(???)<br />
|-<br />
| 0x01<br />
| u8 ThinningCountY(???)<br />
|-<br />
| 0x02<br />
| u16 reserved[3]<br />
|}<br />
<br />
==QTM==<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Declaration<br />
|-<br />
| 0x00<br />
| float DivisorAtZero (???)<br />
|-<br />
| 0x04<br />
| float TranslationX<br />
|-<br />
| 0x08<br />
| float TranslationY<br />
|-<br />
| 0x0C<br />
| float RotationZ<br />
|-<br />
| 0x10<br />
| float HorizontalAngle<br />
|-<br />
| 0x14<br />
| float OptimalDistance<br />
|}<br />
<br />
=Reading=<br />
If 0x1FF81006 is 3 or 4 or 7 or 8 or 9 then the callibration data is read from the EEPROM using the <code>i2c:EEP</code> service command 0x001000C0, using offset 0x000 for HWCAL0, and offset 0x800 for HWCAL1.<br />
Otherwise attempt is made to read <code>CTRNAND:/ro/sys/HWCAL(0|1).dat</code> instead.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=MTX_Registers&diff=21160MTX Registers2020-04-01T23:07:20Z<p>MarcusD: Update MTX bits and interrupts</p>
<hr />
<div>These registers are responsible for controlling how framebuffer data can be DMA'd from the DS GPU, and also for configuring the upscaling matrix.<br />
<br />
=Registers=<br />
<br />
The physical address can be calculated by subtracting 0xEB00000 from the virtual address.<br />
<br />
==Control==<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
|-<br />
| 0x1EC1x000<br />
| [[#MTX_CNT|MTX_CNT]]<br />
| 4<br />
|-<br />
| 0x1EC1x004<br />
| [[#MTX_SIZE|MTX_SIZE]]<br />
| 4<br />
|-<br />
| 0x1EC1x008<br />
| [[#MTX_ACK|MTX_ACK]]<br />
| 4<br />
|-<br />
| 0x1EC1x00C<br />
| [[#MTX_IE|MTX_IE]]<br />
| 4<br />
|-<br />
| 0x1EC1x020<br />
| ???<br />
| 4<br />
|-<br />
|}<br />
<br />
==Matrix unit==<br />
<br />
There are two matrix units, one at +0x200 for vertical (Y) scaling, and the other one at +0x300 for horizontal (X) scaling.<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
! Description<br />
|-<br />
| 0x1EC1xn00<br />
| KRN_WIDTH<br />
| 4<br />
| Kernel width - 1 is written here, 1 <= width <= 8<br />
<br />
This decides how many pixels are written each batch.<br />
|-<br />
| 0x1EC1xn04<br />
| KRN_PATTERN_BITS<br />
| 4<br />
| If the corresponding bit for the current batch iteration index is set then a new pixel is read.<br />
<br />
The amount of set bits determine how many pixels are read each batch. Any bit indexes past KRN_WIDTH are ignored.<br />
<br />
This value is 8 bits, but it has to be written with a 32bit write.<br />
|-<br />
| 0x1EC1xn40<br />
| KRN_MTX<br />
| 0xC0<br />
| int kerneldata[6][8]; - matrix data is written here, height is always 6<br />
|-<br />
|}<br />
<br />
=Descriptions=<br />
<br />
==MTX_CNT==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Enable bit<br />
|-<br />
| 1<br />
| Enable vertical matrix<br />
|-<br />
| 2<br />
| Enable horizontal matrix<br />
|-<br />
| 4<br />
| ???<br />
|-<br />
| 5<br />
| ???<br />
|-<br />
| 8-9<br />
| Input pixel mode? 0 = 4byte color, 1 = 3byte color, 2 = 2byte color, 3 = 2byte color<br />
|-<br />
| 10-11<br />
| Output framebuffer rotation: 0 = normal, 1 = 90° CW (right), 2 = 180° CW (upside down, not mirrored), 3 = 270° CW (left)<br />
|-<br />
| 12<br />
| Output tiling for use with the GPU. When set, the output width and height must be a multiple of 8.<br />
|-<br />
| 15<br />
| Start bit (setting this will eventually raise MTX interrupt 0)<br />
|-<br />
| 16<br />
| Data still available flag (?)<br />
|-<br />
|}<br />
<br />
==MTX_SIZE==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0-8<br />
| Output framebuffer width - 1 is written here, 1 <= width <= 512<br />
|-<br />
| 16-25<br />
| Output framebuffer height - 1 is written here, 1 <= height <= 512<br />
|-<br />
|}<br />
<br />
==MTX_ACK==<br />
<br />
Reading this register will return pending interrupts.<br />
Writing this register will acknowledge pending interrupts where the bits are set.<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| FIFO ready (signal to start DMA)<br />
|-<br />
| 1<br />
| FIFO overrun(?) (occurs if DMA is too slow)<br />
|-<br />
| 2<br />
| FIFO underrun(?) (occurs on VBlank)<br />
|-<br />
|}<br />
<br />
==MTX_IE==<br />
<br />
Interrupt Enable for the above interrupts.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21132I2C Registers2019-12-28T19:24:18Z<p>MarcusD: Clarify where each gyroscope is used (although missing old3DSXL and non-XL new3DS)</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (2DS, new3DSXL, new2DSXL)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21127I2C Registers2019-12-25T23:49:57Z<p>MarcusD: Added more gyro info</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope (old3DS?)<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| Gyroscope (new3DS?)<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 11 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21108I2C Registers2019-11-30T10:46:30Z<p>MarcusD: Fix tilt sensor typo and increased info verbosity</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held up vertically<br />
| rotated left 90° like a steering wheel<br />
| rotated right 90° like a steering wheel<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat on the desk with the screen facing up<br />
| held up vertically<br />
| held up vertically with screen facing upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21107I2C Registers2019-11-30T10:39:30Z<p>MarcusD: s/gyro/tilt sensor/gi</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| 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.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held vertically<br />
| rotated or tilted left<br />
| rotated or tilted right<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat<br />
| held up<br />
| helf up upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21070I2C Registers2019-10-13T09:49:43Z<p>MarcusD: Updated MCU pointer table register info</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| Gyro sampling mode. Bits 0 and 1 control the gyro. If bits 0 or 1 are non-zero then gyro sampling is enabled.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Gyroscope 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held vertically<br />
| rotated or tilted left<br />
| rotated or tilted right<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat<br />
| held up<br />
| helf up upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information (debug pointer table)<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: Red Power LED mode (0 = off, 1 = on)<br />
byte 0x0B: Blue Power LED intensity (0x00 - 0xFF)<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while Power button is held<br />
bit1: unset while HOME button is held<br />
bit2: unset while WiFi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21069I2C Registers2019-10-13T09:26:23Z<p>MarcusD: Updated formatting for MCU register 0x0F and added bits 5-7 documentation</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].<br />
bit01: ShellState<br />
bit03: AdapterState<br />
bit04: BatteryChargeState<br />
bit05: Bottom screen backlight on<br />
bit06: Top screen backlight on<br />
bit07: GPU on(?)<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| Gyro sampling mode. Bits 0 and 1 control the gyro. If bits 0 or 1 are non-zero then gyro sampling is enabled.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Gyroscope 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held vertically<br />
| rotated or tilted left<br />
| rotated or tilted right<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat<br />
| held up<br />
| helf up upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information.<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: power LED related? 0 is off, 1 is red<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while power button is held<br />
bit1: unset while home button is held<br />
bit2: unset while Wifi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
this byte is reset to 0 before an svcBreak takes effect<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21068I2C Registers2019-10-08T19:11:48Z<p>MarcusD: Updated MCU gyro docs</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of these are read via [[MCU_Services|mcu::RTC]]: bit4 = BatteryChargeState. bit3 = AdapterState. bit1 = ShellState.<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| Gyro sampling mode. Bits 0 and 1 control the gyro. If bits 0 or 1 are non-zero then gyro sampling is enabled.<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, pedoometer related(?)<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Gyroscope 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| X (left/right)<br />
| held vertically<br />
| rotated or tilted left<br />
| rotated or tilted right<br />
|-<br />
| Y (forwards/backwards)<br />
| laid flat<br />
| held up<br />
| helf up upside-down<br />
|-<br />
| Z (???)<br />
| ???<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information.<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: power LED related? 0 is off, 1 is red<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while power button is held<br />
bit1: unset while home button is held<br />
bit2: unset while Wifi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
this byte is reset to 0 before an svcBreak takes effect<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=Legacy_FIRM_PXI&diff=21048Legacy FIRM PXI2019-09-22T09:18:43Z<p>MarcusD: Complete the IPC signature for Command 5</p>
<hr />
<div>This page describes the PXI commands for TWL_FIRM/AGB_FIRM.<br />
<br />
{| class="wikitable" border="1"<br />
|-<br />
! Command Header<br />
! Available since system version<br />
! Description<br />
|-<br />
| 0x0001....<br />
| [[1.0.0-0]]<br />
| Shuts down LgyP9, puts ARM9 into a <code>while(1) svcSleepThread(1*1000*1000);</code> loop at the end of <code>main()</code><br />
|-<br />
| 0x00020080<br />
| [[1.0.0-0]]<br />
| PrepareArm9ForTwl(u64 application_titleID) This launches the specified TWL title.<br />
|-<br />
| 0x00030080<br />
| [[1.0.0-0]]<br />
| PrepareArm9ForAgb(u64 application_titleID) This launches the specified GBA VC title. On success, returns u64 GBA VC title exeFS .code length in cmdbuf[2].<br />
|-<br />
| 0x00040080<br />
| [[1.0.0-0]]<br />
| Process9 will eventually wait for the ARM11 to send this command, see [[FIRM|here]]. The command input parameters are not used.<br />
|-<br />
| 0x00050040<br />
| [[1.0.0-0]]<br />
| (u8 unk) Does some lowlevel sd/emmc register setting (different for unk=0 and unk=1). If unk=1, sets a flag, code ran during the pxi main-func loop detects this and calls <code>svcKernelSetState(2,0)</code>.<br />
|-<br />
| 0x0006....<br />
| [[1.0.0-0]]<br />
| (DateTime datetimetoset,u8 shouldRead) - Sets the P9 date/time (calling the same function used by cmd 0xA for this), then reads (if shouldRead is not 0) or writes (if shouldRead is 0) ARM7_RTC_LO/HI registers to or from agbsave_in_ram+0x60. AgbBg doesn't appear to use this command at all.<br />
|-<br />
| 0x0007....<br />
| [[1.0.0-0]]<br />
| Returns u8, IsSdCardInserted maybe?<br />
|-<br />
| 0x0008....<br />
| [[1.0.0-0]]<br />
| Stubbed, returns 0xE0C0EC03...<br />
|-<br />
| 0x0009....<br />
| [[1.0.0-0]]<br />
| Stubbed, returns 0xE0C0EC03...<br />
|-<br />
| 0x000A....<br />
| [[1.0.0-0]]<br />
| Sets Process9's internal date/time, see below.<br />
|-<br />
| 0x000B0240<br />
| [[1.0.0-0]]<br />
| This is used for TWL initialization, prior to using command 0x00020080. Arguments: u8 <2 if card, else 3>, u8 <bit 1 from firmlaunchparams+0x460>, u64 tid, u8 bannerHmac[0x14]<br />
|-<br />
| 0x000C0800<br />
| [[1.0.0-0]]<br />
| This writes the input 0x80-byte ASCII data to [[Flash_Filesystem|nand:/rw/sys/lgy.log]].<br />
|}<br />
<br />
This PXI service seems to be based on [[Development Services PXI]]. Commands 0x8 and 0x9 in both are stubbed with the same function (returns 0xE0C0EC03), commands that seem useless under NATIVE_FIRM have a purpose on legacy FIRMs (command 0xC does some "unnecessary copying to stack" on NATIVE_FIRM, but this same copy (0x80-bytes) is used to write to lgy.log on legacy FIRMs), and commands that are essential (and only useful) on legacy FIRMs (0x2 and 0x3) are stubbed completely on NATIVE_FIRM.<br />
<br />
=Command 0x2=<br />
This does the following:<br />
* Waits for an u8 state field to become non-zero.<br />
* Clears DSi memory, etc.<br />
* Loads the DS(i) application specified by the command request titleID. If this fails, it immediately returns the error for this.<br />
* Initializes the DSi memory at 0x02fe7000 and 0x02fffc00.<br />
* Loads the TWL launcher located at physical address [[Memory_layout|0x27C00000]], which was written there by the TwlBg ARM11 process.<br />
* Loads the TWL bootloader, see [[FIRM|here]].<br />
* Initializes DSi memory/keys, [[IO_Registers|0x10018000]] registers, etc.<br />
* Writes value 0x3 to [[CONFIG_Registers|REG_BOOTENV]], and value 0x1 to an u8 state field.<br />
* Uses [[SVC|svcSignalEvent]], then returns.<br />
<br />
=Command 0xA=<br />
This takes 3 arguments, which are the following structure packed into 12 bytes (no padding):<br />
s32 year;<br />
s8 month;<br />
s8 day;<br />
s8 day_of_week; // Sunday = 0, up to Saturday = 6<br />
s8 hour;<br />
s8 minute;<br />
s8 second;<br />
s16 ms;<br />
This should be the current date/time (AgbBg seems to get it from MCU); it's converted from this structure into milliseconds, and saved into .data. Another .data variable is set from svcGetSystemTick by this command, and another function in (LGY) P9 essentially does <code>return date_time_set_from_this_command + ticks_to_ms(svcGetSystemTick() - systemtick_from_this_command);</code> to get the current date&time.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPIO_Registers&diff=21027GPIO Registers2019-07-23T20:42:45Z<p>MarcusD: Add legacy RTC registers</p>
<hr />
<div>= Registers =<br />
<br />
== GPIO ==<br />
<br />
{| class="wikitable" border="1"<br />
! Name<br />
! Address<br />
! Width<br />
! GPIO [[GPIO_Services|bitmasks]] associated with this register<br />
|-<br />
| GPIO_DATA0<br />
| 0x10147000<br />
| 2<br />
| 0x1, 0x2, 0x4<br />
|-<br />
| GPIO_DATA1<br />
| [[#0x10147010|0x10147010]]<br />
| 4<br />
| 0x8, 0x10<br />
|-<br />
| GPIO_DATA2<br />
| 0x10147014<br />
| 2<br />
| 0x20<br />
|-<br />
| GPIO_DATA3<br />
| 0x10147020<br />
| 2<br />
| 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000<br />
|-<br />
| GPIO_DATA3_INTERRUPT_CLEAR<br />
| 0x10147022<br />
| 2<br />
| 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000<br />
|-<br />
| ??<br />
| 0x10147024<br />
| 2<br />
| 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000<br />
|-<br />
| ?<br />
| [[#0x10147026|0x10147026]]<br />
| 2<br />
| ?<br />
|-<br />
| GPIO_DATA4<br />
| 0x10147028<br />
| 2<br />
| 0x40000<br />
|}<br />
<br />
== Legacy RTC ==<br />
{| class="wikitable" border="1"<br />
! Name<br />
! Address<br />
! Width<br />
! Description<br />
|-<br />
| RTC_CNT (?)<br />
| 0x10147100<br />
| 2<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147110<br />
| 1<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147111<br />
| 1<br />
| ???<br />
|-<br />
| RTC_RAW<br />
| 0x10147120<br />
| 4<br />
| Byte-wise bit-swapped (bit7 is bit0, etc.) BCD RTC (byte0 = seconds, byte1 = minutes, byte2 = hours, byte3 = day(?))<br />
|-<br />
| RTC_?<br />
| 0x10147124<br />
| 4<br />
| RTC offset?<br />
|-<br />
| RTC_?<br />
| 0x10147130<br />
| 4<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147134<br />
| 4<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147140<br />
| 4<br />
| Some sort of byte-wise bit-swapped seconds counter<br />
|-<br />
| RTC_?<br />
| 0x10147150<br />
| 1<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147151<br />
| 1<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147160<br />
| 4<br />
| ???<br />
|-<br />
| RTC_?<br />
| 0x10147164<br />
| 4<br />
| ???<br />
|-<br />
|}<br />
<br />
= Descriptions =<br />
<br />
== 0x10147010 ==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 24<br />
| Enable/disable? GPIO interrupt 0x64 (bitmask 0x8)<br />
|-<br />
| 25<br />
| Enable/disable? GPIO interrupt 0x66 (bitmask 0x10)<br />
|}<br />
<br />
== 0x10147026 ==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0-8<br />
| ?<br />
|-<br />
| 9<br />
| Enable/disable interrupt 0x71.<br />
|-<br />
| 10-15<br />
| ?<br />
|}<br />
<br />
== GPIO_DATA ==<br />
<br />
=== GPIO_DATA0 ===<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0-2<br />
| Used for GPIO [[GPIO_Services|bitmask]] 0x7.<br />
|-<br />
| 1<br />
| DS EXTKEYIN Pen down (0 = touching, 1 = not touching)<br />
|-<br />
| 3<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|-<br />
| 4<br />
| Only used by [[Bootloader|Boot11]].<br />
|-<br />
| 5-15<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|}<br />
<br />
=== GPIO_DATA1 ===<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0-1<br />
| Used for GPIO [[GPIO_Services|bitmask]] 0x18.<br />
|-<br />
| 2-31<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|}<br />
<br />
=== GPIO_DATA2 ===<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| Used for GPIO [[GPIO_Services|bitmask]] 0x20.<br />
|-<br />
| 1-15<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|}<br />
<br />
=== GPIO_DATA3 ===<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0-11<br />
| Used for GPIO [[GPIO_Services|bitmask]] 0x3FFC0.<br />
|-<br />
| 12-31<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|}<br />
<br />
=== GPIO_DATA4 ===<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| Used for GPIO [[GPIO_Services|bitmask]] 0x40000.<br />
|-<br />
| 1-15<br />
| Unused by GPIO-sysmodule and TwlBg.<br />
|}<br />
<br />
= Default values =<br />
<br />
After bootrom initialization, these are the values of the registers:<br />
<br />
{| class="wikitable" border="1"<br />
! Address<br />
! Value<br />
|-<br />
| 0x10147000<br />
| 0x0003<br />
|-<br />
| 0x10147010<br />
| 0x00000002<br />
|-<br />
| 0x10147014<br />
| 0x0000<br />
|-<br />
| 0x10147020<br />
| 0x00000DFB<br />
|-<br />
| 0x10147024<br />
| 0x00000000<br />
|-<br />
| 0x10147028<br />
| 0x0000<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=CONFIG11_Registers&diff=21024CONFIG11 Registers2019-07-18T10:16:59Z<p>MarcusD: add some legacy HID emulation info</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_SHAREDWRAM_32K_DATA|CFG11_SHAREDWRAM_32K_DATA]]<0-7><br />
| 0x10140000<br />
| 1*8<br />
| Boot11, Process9, [[DSP Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_SHAREDWRAM_32K_CODE|CFG11_SHAREDWRAM_32K_CODE]]<0-7><br />
| 0x10140008<br />
| 1*8<br />
| Boot11, Process9, [[DSP Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10140100<br />
| 2<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10140102<br />
| 2<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_FIQ_CNT|CFG11_FIQ_CNT]]<br />
| 0x10140104<br />
| 1<br />
| Kernel11.<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10140105<br />
| 1<br />
| Kernel11.<br />
|-<br />
| style="background: green" | Yes<br />
| Related to [[HID_Registers|HID_?]]<br />
| 0x10140108<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| Related to [[HID_Registers|HID_?]]<br />
| 0x1014010C<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_GPUPROT|CFG11_GPUPROT]]<br />
| 0x10140140<br />
| 4<br />
| Kernel11<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_WIFICNT|CFG11_WIFICNT]]<br />
| 0x10140180<br />
| 1<br />
| TwlBg, [[NWM Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_SPI_CNT|CFG11_SPI_CNT]]<br />
| 0x101401C0<br />
| 2<br />
| [[SPI Services]], TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10140200<br />
| 4<br />
|<br />
|-style="border-top: double"<br />
| style="background: red" | No<br />
| Clock related?<br />
| 0x10140400<br />
| 1<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| Clock related?<br />
| 0x10140410<br />
| 4<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| [[#CFG11_BOOTROM_OVERLAY_CNT|CFG11_BOOTROM_OVERLAY_CNT]]<br />
| 0x10140420<br />
| 1<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| [[#CFG11_BOOTROM_OVERLAY_VAL|CFG11_BOOTROM_OVERLAY_VAL]]<br />
| 0x10140424<br />
| 4<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| ?<br />
| 0x10140428<br />
| 4<br />
|<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| [[#CFG11_SOCINFO|CFG11_SOCINFO]]<br />
| 0x10140FFC<br />
| 2<br />
| Boot11, Kernel11<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| CFG11_GPU_STATUS<br />
| 0x10141000<br />
| 2<br />
| Kernel11, TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| CFG11_PTM_0<br />
| 0x10141008<br />
| 4<br />
| [[PTM Services]], [[PDN Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| CFG11_PTM_1<br />
| 0x1014100C<br />
| 4<br />
| [[PTM Services]], TwlBg, [[PDN Services]]<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLMODE_0|CFG11_TWLMODE_0]]<br />
| 0x10141100<br />
| 2<br />
| TwlProcess9, TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLMODE_1|CFG11_TWLMODE_1]]<br />
| 0x10141104<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLMODE_2|CFG11_TWLMODE_2]]<br />
| 0x10141108<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLMODE_HID|CFG11_TWLMODE_HID]]<br />
| 0x1014110A<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_WIFIUNK|CFG11_WIFIUNK]]<br />
| 0x1014110C<br />
| 1<br />
| [[NWM Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLAGB_HIDEMU_MASK|CFG11_TWLAGB_HIDEMU_MASK]]<br />
| 0x10141110<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_TWLAGB_HIDEMU_PAD|CFG11_TWLAGB_HIDEMU_PAD]]<br />
| 0x10141112<br />
| 2<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_CODEC|CFG11_CODEC_0]]<br />
| 0x10141114<br />
| 2<br />
| [[Codec Services]], TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_CODEC|CFG11_CODEC_1]]<br />
| 0x10141116<br />
| 2<br />
| [[Codec Services]], TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10141118<br />
| 1<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10141119<br />
| 1<br />
| TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| ?<br />
| 0x10141120<br />
| 1<br />
| TwlBg<br />
|-<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| [[#CFG11_GPU_CNT|CFG11_GPU_CNT]]<br />
| 0x10141200<br />
| 4<br />
| Boot11, Kernel11, [[PDN Services]], TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_GPU_CNT2|CFG11_GPU_CNT2]]<br />
| 0x10141204<br />
| 4<br />
| Boot11, Kernel11, TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| CFG11_GPU_FCRAM_CNT<br />
| 0x10141210<br />
| 2<br />
| Kernel11, TwlBg<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_CODEC_CNT|CFG11_CODEC_CNT]]<br />
| 0x10141220<br />
| 1<br />
| Boot11, TwlBg, [[PDN Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_CAMERA_CNT|CFG11_CAMERA_CNT]]<br />
| 0x10141224<br />
| 1<br />
| [[PDN Services]]<br />
|-<br />
| style="background: green" | Yes<br />
| [[#CFG11_DSP_CNT|CFG11_DSP_CNT]]<br />
| 0x10141230<br />
| 1<br />
| Process9, [[PDN Services]]<br />
|-style="border-top: double"<br />
| style="background: red" | No<br />
| [[#CFG11_MPCORE_CLKCNT|CFG11_MPCORE_CLKCNT]]<br />
| 0x10141300<br />
| 2<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| [[#CFG11_MPCORE_CNT|CFG11_MPCORE_CNT]]<br />
| 0x10141304<br />
| 2<br />
| NewKernel11<br />
|-<br />
| style="background: red" | No<br />
| [[#CFG11_MPCORE_BOOTCNT<0-3>|CFG11_MPCORE_BOOTCNT]]<0-3><br />
| 0x10141310<br />
| 1*4<br />
| NewKernel11<br />
|}<br />
<br />
== CFG11_SHAREDWRAM_32K_DATA ==<br />
Used for mapping 32K chunks of shared WRAM for DSP data.<br />
<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
|-<br />
| 0-1<br />
| Master (0=ARM9?, 1=ARM11?, 2 or 3=DSP/data)<br />
|-<br />
| 2-4<br />
| Offset (0..7) (slot 0..7) (LSB of address in 32Kbyte units)<br />
|-<br />
| 5-6<br />
| Not used (0)<br />
|-<br />
| 7<br />
| Enable (0=Disable, 1=Enable)<br />
|}<br />
<br />
== CFG11_SHAREDWRAM_32K_CODE ==<br />
Used for mapping 32K chunks of shared WRAM for DSP data.<br />
<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
|-<br />
| 0-1<br />
| Master (0=ARM9?, 1=ARM11?, 2 or 3=DSP/code)<br />
|-<br />
| 2-4<br />
| Offset (0..7) (slot 0..7) (LSB of address in 32Kbyte units)<br />
|-<br />
| 5-6<br />
| Not used (0)<br />
|-<br />
| 7<br />
| Enable (0=Disable, 1=Enable)<br />
|}<br />
<br />
== CFG11_FIQ_CNT ==<br />
Writing bit1 to this register disables FIQ interrupts.<br />
<br />
This bit is set upon receipt of a FIQ interrupt and when [[SVC|svcUnbindInterrupt]] is called on the FIQ-abstraction [[ARM11_Interrupts#Private_Interrupts|software interrupt]] for the current core.<br />
It is cleared when binding that software interrupt to an event and just before that event is signaled.<br />
<br />
== CFG11_SPI_CNT ==<br />
When the corresponding bit is 0, the bus has to be accessed using the DS SPI registers. Otherwise it has to be accessed using the 3DS SPI registers.<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| Enable [[SPI Registers]] 0x10160000.<br />
|-<br />
| 1<br />
| Enable [[SPI Registers]] 0x10142000.<br />
|-<br />
| 2<br />
| Enable [[SPI Registers]] 0x10143000.<br />
|}<br />
<br />
== CFG11_BOOTROM_OVERLAY_CNT ==<br />
Bit0: Enable bootrom overlay functionality.<br />
<br />
== CFG11_BOOTROM_OVERLAY_VAL ==<br />
The 32-bit value to overlay data-reads to bootrom with. See [[#CFG11_MPCORE_BOOTCNT|CFG11_MPCORE_BOOTCNT]].<br />
<br />
== CFG11_SOCINFO ==<br />
Read-only register.<br />
<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
! Used by<br />
|-<br />
| 0<br />
| 1 on both Old3DS and New3DS.<br />
| Boot11<br />
|-<br />
| 1<br />
| 1 on New3DS.<br />
| Kernel11<br />
|-<br />
| 2<br />
| Clock modifier: if set, use a 3x multiplier, otherwise 2x<br />
| Kernel11<br />
|}<br />
<br />
== CFG11_MPCORE_CLKCNT ==<br />
This is used for configuring the New3DS ARM11 CPU clock-rate. This register is New3DS-only: reading from here on Old3DS always returns all-zeros even when one tried writing data here prior to the read.<br />
<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
|-<br />
| 0<br />
| Enable clock multiplier? This must be set to 1 before writing a non-zero value to bit1-2, otherwise freeze. This enables the New 3DS FCRAM extension.<br />
|-<br />
| 1-2<br />
| Clock multiplier (0=1x, 1=2x, 2=3x, 3=hang)<br />
|-<br />
| 15<br />
| Busy<br />
|}<br />
<br />
[[SVC#KernelSetState|svcKernelSetState]] type10, only implemented on New3DS, uses this register. That code writes the following values to this register, depending on the input Param0 bit0 state, and the state of CFG11_SOCINFO:<br />
{| class="wikitable" border="1"<br />
! Register value<br />
! Higher-clockrate bit set in svcKernelSetState Param0<br />
! CFG11_SOCINFO bit2 set<br />
! MPCore timer/watchdog prescaler value, prior to subtracting it by 0x1 when writing it into hw/state<br />
! Clock-rate multiplier<br />
! Description<br />
|-<br />
| 0x01<br />
| No<br />
| Yes<br />
| 0x01<br />
| 1x<br />
| 268MHz<br />
|-<br />
| 0x02<br />
| No<br />
| No<br />
| 0x01<br />
| 1x<br />
| 268MHz<br />
|-<br />
| 0x05<br />
| Yes<br />
| Yes<br />
| 0x03<br />
| 3x<br />
| 804MHz<br />
|-<br />
| 0x03<br />
| Yes<br />
| No<br />
| 0x02<br />
| 2x<br />
| 536MHz (tested on New3DS)<br />
|}<br />
<br />
Note that the above CFG11_SOCINFO bit is 1 on New3DS, and 0 on Old3DS. Since this SVC is only available with the New3DS ARM11-kernel, the only additional available clock-rate is 804MHz when running on New3DS(with official kernel code).<br />
<br />
The following register value(s) were tested on New3DS by patching the kernel:<br />
* 0x00: Entire system hangs.<br />
* 0x02: Entire system hangs.<br />
* 0x03: ARM11 runs at 536MHz.<br />
* 0x04: Entire system hangs.<br />
* 0x06: Entire system hangs.<br />
* 0x07: Same result as 0x05.<br />
* 0x08: Entire system hangs.<br />
* 0x09: Entire system hangs.<br />
* 0x0A: Entire system hangs.<br />
* 0x0B: Same result as 0x03.<br />
* 0x0C: Entire system hangs.<br />
* 0x0D: Same result as 0x05.<br />
* 0x0E: Entire system hangs.<br />
* 0x0F: Same result as 0x05.<br />
* 0x1F, 0x2F, 0x4F, 0x8F, 0xFF: Same result as 0x05.<br />
<br />
== CFG11_MPCORE_CNT ==<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
|-<br />
| 0<br />
| ?<br />
|-<br />
| 8<br />
| ?<br />
|}<br />
<br />
Kernel11 sets this to 0x101 when bit 2 in [[#CFG11_SOCINFO|CFG11_SOCINFO]] is set otherwise 1.<br />
<br />
== CFG11_MPCORE_BOOTCNT<0-3> ==<br />
{| class="wikitable" border="1"<br />
! Bits<br />
! Description<br />
|-<br />
| 0<br />
| Enable bootrom instruction overlay, maybe? This bit is only writable for core2 and core3.<br />
|-<br />
| 1<br />
| Enable bootrom data overlay. This bit is only writable for core2 and core3.<br />
|-<br />
| 4<br />
| Has core booted maybe?<br />
|-<br />
| 5<br />
| Always 1?<br />
|}<br />
<br />
The normal ARM11 bootrom checks cpuid and hangs if cpuid >= 2. This is a problem when booting the 2 additional New3DS ARM11 MPCores. NewKernel11 solves this by using a hardware feature to overlay the bootrom with a configurable branch to a kernel function. This overlay feature was added with the New3DS.<br />
<br />
Bit1 in register above enables a bootrom data-override for physical addresses 0xFFFF0000-0xFFFF1000 and 0x10000-0x11000. All _data reads_ made to those regions now read the 32-bit value provided in [[#CFG11_BOOTROM_OVERLAY_VAL|CFG11_BOOTROM_OVERLAY_VAL]].<br />
<br />
Bit0 enables a bootrom instruction-overlay which means that _instruction reads_ made to the bootrom region are overridden. We have not been able to dump what instructions are actually placed at bootrom by this switch (because reading the area only yields data-reads). Jumping randomly into the 0xFFFF0000-0xFFFF1000 region works fine and jumps to the value provided by the data overlay [[#CFG11_BOOTROM_OVERLAY_VAL|CFG11_BOOTROM_OVERLAY_VAL]]. Thus we may predict that the entire bootrom region is filled by:<br />
ldr pc, [pc]<br />
<br />
Or equivalent. However, jumping to some high addresses such as 0xFFFF0FF0+ will crash the core. This may be explained by prefetching in the ARM pipeline, and might help us identify what instructions are placed by the instruction-overlay.<br />
<br />
==CFG11_GPUPROT==<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Bits<br />
! Description<br />
|-<br />
| style="background: green" | Yes<br />
| 3-0<br />
| Old FCRAM DMA cutoff size, 0 = no protection.<br />
|-<br />
| style="background: red" | No<br />
| 7-4<br />
| New FCRAM DMA cutoff size, 0 = no protection.<br />
|-<br />
| style="background: green" | Yes<br />
| 8<br />
| AXIWRAM protection, 0 = accessible.<br />
|-<br />
| style="background: red" | No<br />
| 10-9<br />
| QTM DMA cutoff size<br />
|-<br />
| style="background: green" | Yes<br />
| 31-11<br />
| Zeroes<br />
|}<br />
<br />
For the old FCRAM DMA cutoff, it protects starting from 0x28000000-(0x800000*x) until end of FCRAM. There is no way to protect the first 0x800000-bytes.<br />
<br />
For the new FCRAM DMA cutoff, it protects starting from 0x30000000-(0x800000*x) until end of FCRAM. When the old FCRAM cutoff is set to non-zero, the first 0x800000-bytes bytes of new FCRAM are protected.<br />
<br />
On New3DS the old+new FCRAM cutoff can be used at the same time, however this isn't done officially.<br />
<br />
For the QTM DMA cutoff, it protects starting from 0x1F400000-(0x100000*x) until end of QTM mem.<br />
<br />
On cold boot this reg is set to 0.<br />
<br />
When this register is set to value 0, the GPU can access the entire FCRAM, AXIWRAM, and on New3DS all QTM-mem.<br />
<br />
[[SVC|Initialized]] during kernel boot, and used with [[SVC]] 0x59 which was implemented with [[11.3.0-36|v11.3]].<br />
<br />
==CFG11_WIFICNT==<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Bits<br />
! Description<br />
|-<br />
| style="background: green" | Yes<br />
| 0<br />
| Enable wifi subsystem<br />
|}<br />
<br />
==CFG11_TWLMODE_0==<br />
Observed 0x8001 when running under TWL_ and AGB_FIRM, 0 NATIVE_FIRM.<br />
<br />
This address is poked from ARM7 to signal that it has booted and begun executing code. The ARM7-mode address for this register is 0x4700000.<br />
<br />
The very last 3DS-mode register poke the [[FIRM|TWL_FIRM]] Process9 does before it gets switched into TWL-mode, is writing 0x8000 to this register. Before writing this register, TWL Process9 waits for ARM7 to change the value of this register. The Process9 code for this runs from ITCM, since switching into TWL-mode includes remapping all ARM9 physical memory.<br />
<br />
Writing 0x8000 to here from the ARM9 with NATIVE_FIRM running doesn't seem to do anything, other reg-pokes likely need done first.<br />
<br />
==CFG11_TWLMODE_1==<br />
Observed 0x8000 when running under TWL_FIRM, 0 NATIVE_FIRM.<br />
<br />
==CFG11_TWLMODE_2==<br />
Bitfield.<br />
<br />
==CFG11_TWLMODE_HID==<br />
The value of this register is copied to [[HID_Registers|HID_?]] under certain conditions.<br />
<br />
==CFG11_WIFIUNK==<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Bits<br />
! Description<br />
|-<br />
| style="background: green" | Yes<br />
| 4<br />
| Wifi-related? Set to 1 very early in NWM-module.<br />
|}<br />
<br />
==CFG11_TWLAGB_HIDEMU_MASK==<br />
Set bits will use the corresponding values from [[#CFG11_TWLAGB_HIDEMU_PAD|CFG11_TWLAGB_HIDEMU_PAD]] instead of allowing the hardware to read it from [[HID_Registers#HID_PAD|HID_PAD]].<br />
<br />
This is set to 0x1FFF (all buttons and the debug key) and [[#CFG11_TWLAGB_HIDEMU_PAD|CFG11_TWLAGB_HIDEMU_PAD]] is set to 0 when the "Close this software and return to HOME Menu?" dialog is shown to prevent the button presses from propagating to the DS/GBA CPU.<br />
<br />
==CFG11_TWLAGB_HIDEMU_PAD==<br />
Works the same way as [[HID_Registers#HID_PAD|HID_PAD]], but the values set here are only replaced in the HID_PAD seen by the DS/GBA CPU when the corresponding bits in [[#CFG11_TWLAGB_HIDEMU_MASK|CFG11_TWLAGB_HIDEMU_MASK]] are set.<br />
<br />
==CFG11_GPU_CNT==<br />
This one seems to control the LCD/GPU/Backlight.<br />
<br />
Bit0: main (?) nRESET (active low), unset to reset (when not on reset, external GPU registers at 0x10400000+ are enabled).<br />
When this is unset VRAM is not accessible and triggers exceptions.<br />
<br />
Bits 1..6: other nRESET bits.<br />
<br />
Bit16: Enable/Turn on LCD backlight.<br />
<br />
PDN uses a 12 Arm11 cycle delay to deassert reset.<br />
<br />
==CFG11_GPU_CNT2==<br />
Bit0: Power on GPU?<br />
<br />
==CFG11_GPU_FCRAM_CNT==<br />
Bit1: Enable/disable FCRAM.<br />
Bit2: Enable/disable operation in progress.<br />
<br />
==CFG11_CODEC==<br />
The following is the only time the ARM11 CODEC module uses any 0x1EC41XXX registers. In one case CODEC module clears bit1 in register 0x1EC41114, in the other case CODEC module sets bit1 in registers 0x1EC41114 and 0x1EC41116.<br />
<br />
==CFG11_CODEC_CNT==<br />
This is the power register used for the [[CFG11_Services|PDN]] CODEC service.<br />
<br />
bit0 = unknown, bit1 = turn on/off DSP, rest = always 0.<br />
<br />
==CFG11_CAMERA_CNT==<br />
This is the power register used for the [[CFG11_Services|PDN]] camera service.<br />
<br />
bit0 = unknown, bit1 = turn on/off cameras, rest = always 0.<br />
<br />
==CFG11_DSP_CNT==<br />
This is the power register used for the [[CFG11_Services|PDN Services]] DSP service.<br />
<br />
bit0: NRESET (active low). Unset to reset/hold reset.<br />
bit1: enable bit.<br />
<br />
PDN services holds reset for 0x30 Arm11 cycles.</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=IO_Registers&diff=21023IO Registers2019-07-17T04:08:20Z<p>MarcusD: Add distinction</p>
<hr />
<div>= Overview =<br />
<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! A9/A11<br />
! Category<br />
! Physaddr<br />
! Used by<br />
! Comments<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[CONFIG9 Registers]]<br />
| 0x10000000<br />
| Boot9, Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[IRQ Registers]]<br />
| 0x10001000<br />
| Boot9, Process9, Kernel9<br />
| ARM9 Interrupt Masking<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[NDMA Registers]]<br />
| 0x10002000<br />
| Boot9, Process9<br />
| DMA Engine<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[TIMER Registers]]<br />
| 0x10003000<br />
| Boot9, Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[CTRCARD Registers]]<br />
| 0x10004000 / 0x10005000<br />
| Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[EMMC Registers]]<br />
| 0x10006000 / 0x10007000<br />
| Boot9, Process9, NewKernel9Loader<br />
| 0x10007000 is normally not enabled on retail, all-zeros when read.<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[PXI Registers]]<br />
| 0x10008000<br />
| Boot9, Process9<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[AES Registers]]<br />
| 0x10009000<br />
| Boot9, Process9, NewKernel9Loader<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[SHA Registers]]<br />
| 0x1000A000<br />
| Boot9, Process9, NewKernel9Loader<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[RSA Registers]]<br />
| 0x1000B000<br />
| Boot9, Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[Corelink DMA Engines|XDMA Registers]]<br />
| 0x1000C000<br />
| Boot9, Kernel9<br />
| [http://infocenter.arm.com/help/topic/com.arm.doc.subset.primecell.system/index.html CoreLink™ DMA-330 r0p0] (two channels).<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[SPICARD Registers]]<br />
| 0x1000D800<br />
| Process9<br />
|<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A9<br />
| [[CONFIG Registers]]<br />
| 0x10010000<br />
| Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| PRNG Registers<br />
| 0x10011000<br />
| Boot9, Process9<br />
| Used as entropy-source for seeding random number generators.<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[OTP Registers]]<br />
| 0x10012000<br />
| Boot9, Kernel9, NewKernel9Loader<br />
| Top secret.<br />
|-<br />
| style="background: green" | Yes<br />
| A9<br />
| [[ARM7|ARM7 Registers]]<br />
| 0x10018000<br />
| TwlProcess9<br />
| Used to setup the ARM7 core for AGB/TWL<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| Debug WIFI SDIO Registers?<br />
| 0x10100000<br />
| <br />
| An SDIO controller is mapped here, NWM references this controller but doesn't have access to it.<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[HASH Registers]]<br />
| 0x10101000<br />
| [[Filesystem services]]<br />
| These registers function the same as the [[SHA Registers]], with the exception of the FIFO being located at 0x10301000.<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[Y2R Registers]]<br />
| 0x10102000<br />
| [[Camera Services]]<br />
| y2r<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[CSND Registers]]<br />
| 0x10103000<br />
| TwlBg, [[Codec Services]], [[CSND Services]], [[DSP Services]]<br />
| Sound hardware.<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MTX_Registers|LgyFb bottom screen]]<br />
| 0x10110000<br />
| TwlBg<br />
| IO registers used to access legacy output framebuffer, as well as configure the upscaling filter.<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MTX_Registers|LgyFb top screen]]<br />
| 0x10111000<br />
| TwlBg<br />
| IO registers used to access legacy output framebuffer, as well as configure the upscaling filter.<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[Camera Registers]] <br />
| 0x10120000<br />
| [[Camera Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[Camera Registers]]<br />
| 0x10121000<br />
| [[Camera Services]]<br />
| Mirror of 0x10120000?<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[WIFI Registers]]<br />
| 0x10122000<br />
| [[NWM Services]]<br />
| WIFI SDIO bus registers<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| ?<br />
| 0x10123000<br />
| [[NWM Services]]<br />
| WIFI?<br />
|-style="border-top: double"<br />
| style="background: red" | No<br />
| A11/A9<br />
| [[MVD Registers]]<br />
| 0x10130000<br />
| [[MVD Services]]<br />
| <br />
|-<br />
| style="background: red" | No<br />
| A11/A9<br />
| [[MVD Registers]]<br />
| 0x10131000<br />
| [[MVD Services]]<br />
| <br />
|-<br />
| style="background: red" | No<br />
| A11/A9<br />
| [[MVD Registers]]<br />
| 0x10132000<br />
| [[MVD Services]]<br />
| <br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[CONFIG11 Registers]]<br />
| 0x10140000<br />
| Process9, Boot11, Kernel11, TwlBg, [[DSP Services]], [[NWM Services]], [[SPI Services]]<br />
| Power management. <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[CONFIG11 Registers]]<br />
| 0x10141000<br />
| Process9, Boot11, Kernel11, TwlBg, [[Codec Services]], [[NWM Services]], [[SPI Services]], [[PDN Services]]<br />
| Power management<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[SPI Registers]]<br />
| 0x10142000<br />
| TwlBg, [[SPI Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[SPI Registers]]<br />
| 0x10143000<br />
| TwlBg, dmnt Module<br />
| Debugger related?<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[I2C Registers]]<br />
| 0x10144000<br />
| Boot11, Kernel11, TwlBg, [[I2C Services]]<br />
| 3DS I2C interface (MCU + Cameras + LCD)<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[CODEC Registers]]<br />
| 0x10145000<br />
| TwlBg, [[Codec Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[HID Registers]]<br />
| 0x10146000<br />
| Boot9, Boot11, Kernel11, TwlBg, [[HID Services]], dlp Services<br />
| See [[PAD]].<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[GPIO Registers]]<br />
| 0x10147000<br />
| Boot11, TwlBg, [[GPIO Services]], [[DSP Services]](v0)<br />
| <br />
|- <br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[I2C Registers]]<br />
| 0x10148000<br />
| TwlBg, [[I2C Services]]<br />
| 3DS I2C interface (Gyro + IR)<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[SPI Registers]]<br />
| 0x10160000<br />
| Boot9, TwlBg, [[SPI Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[I2C Registers]]<br />
| 0x10161000<br />
| Boot11, TwlBg, [[I2C Services]]<br />
| TWL I2C interface (MCU + Cameras)<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MIC Registers]]<br />
| 0x10162000<br />
| [[MIC Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[PXI Registers]]<br />
| 0x10163000<br />
| Boot11, Kernel11, TwlBg, [[PXI Services]]<br />
| <br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[NTRCARD Registers]]<br />
| 0x10164000<br />
| Boot9, Process9<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10165000<br />
| [[MP Services]]<br />
|<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10170000<br />
| [[MP Services]]<br />
| NTR WIFI Registers, see [http://problemkaputt.de/gbatek.htm#dswirelesscommunications GBATek].<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10171000<br />
| [[MP Services]]<br />
| NTR WIFI Registers (mirror)<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
|?<br />
| 0x10172000<br />
|?<br />
| NTR WIFI Unused?<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
|?<br />
| 0x10173000<br />
|?<br />
| NTR WIFI Unused?<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10174000<br />
| [[MP Services]]<br />
| NTR WIFI RAM<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10175000<br />
|?<br />
| NTR WIFI RAM<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10176000<br />
|?<br />
| NTR WIFI Registers (mirror)<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10177000<br />
|?<br />
| NTR WIFI Registers (mirror)<br />
|-<br />
| style="background: green" | Yes<br />
| A11/A9<br />
| [[MP Registers]]<br />
| 0x10178000 - 0x10180000<br />
| [[MP Services]]<br />
| NTR WIFI WS1 Region<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11<br />
| [[Corelink DMA Engines|CDMA]]<br />
| 0x10200000<br />
| Boot11, Kernel11<br />
| [http://infocenter.arm.com/help/topic/com.arm.doc.subset.primecell.system/index.html CoreLink™ DMA-330 r0p0] (eight channels). Only used by bootrom on New3DS.<br />
|-<br />
| style="background: green" | Yes<br />
| A11<br />
| ?<br />
| 0x10201000<br />
| TwlBg<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A11<br />
| [[LCD Registers]]<br />
| 0x10202000<br />
| TwlBg, Kernel11, [[GSP Services]]<br />
|<br />
|-<br />
| style="background: green" | Yes<br />
| A11<br />
| [[DSP Registers]]<br />
| 0x10203000<br />
| [[DSP Services]]<br />
| see the "DSi XpertTeak" section in [http://problemkaputt.de/gba.htm no$gba] help.<br />
|-<br />
| style="background: green" | Yes<br />
| A11<br />
| <br />
| 0x10204000<br />
| <br />
|<br />
|-style="border-top: double"<br />
| style="background: red" | No<br />
| A11<br />
| [[Corelink DMA Engines|CDMA]]<br />
| 0x10206000<br />
| NewKernel11<br />
| [http://infocenter.arm.com/help/topic/com.arm.doc.ddi0424d/index.html CoreLink™ DMA-330 r1p2] (eight channels). This is the DMA engine actually being used by the New3DS ARM11 kernel.<br />
|-<br />
| style="background: red" | No<br />
| A11<br />
| [[MVD Registers]]<br />
| 0x10207000<br />
| [[MVD Services]]<br />
| New 3DS only?<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11<br />
| AXI<br />
| 0x1020F000<br />
| TwlBg, [[GSP Services]]<br />
| [http://infocenter.arm.com/help/topic/com.arm.doc.ddi0422a/CHDGHIID.html CoreLink™ NIC-301 r1p0].<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11<br />
| DMA region<br />
| 0x10300000-0x10400000<br />
|<br />
| CDMA wants these addresses. Most pages in this region correspond to the same respective pages in the 0x10100000-0x10200000 region. The HASH FIFO register is located at 0x10301000 only.<br />
|-style="border-top: double"<br />
| style="background: green" | Yes<br />
| A11<br />
| [[GPU/External_Registers|GPU Registers]]<br />
| 0x10400000<br />
| Boot11, Kernel11, [[GSP Services]]<br />
||<br />
|}<br />
<br />
IO registers starting at physical address 0x10200000 are not accessible from the ARM9 (which includes all LCD/GPU registers). It seems IO registers below physical address 0x10100000 are not accessible from the ARM11 bus.<br />
<br />
ARM11 kernel virtual address mappings for these registers varies for different builds. For ARM11 user mode applications you have:<br />
physaddr = virtaddr - 0x1EC00000 + 0x10100000</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=MTX_Registers&diff=21022MTX Registers2019-07-17T03:56:13Z<p>MarcusD: Document more matrix unit bits</p>
<hr />
<div>These registers are responsible for controlling how framebuffer data can be DMA'd from the DS GPU, and also for configuring the upscaling matrix.<br />
<br />
=Registers=<br />
<br />
The physical address can be calculated by subtracting 0xEB00000 from the virtual address.<br />
<br />
==Control==<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
|-<br />
| 0x1EC1x000<br />
| [[#MTX_CNT|MTX_CNT]]<br />
| 4<br />
|-<br />
| 0x1EC1x004<br />
| [[#MTX_SIZE|MTX_SIZE]]<br />
| 4<br />
|-<br />
| 0x1EC1x008<br />
| [[#MTX_ACK|MTX_ACK]]<br />
| 4<br />
|-<br />
| 0x1EC1x00C<br />
| ???<br />
| 4<br />
|-<br />
| 0x1EC1x020<br />
| ???<br />
| 4<br />
|-<br />
|}<br />
<br />
==Matrix unit==<br />
<br />
There are two matrix units, one at +0x200 for vertical (Y) scaling, and the other one at +0x300 for horizontal (X) scaling.<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
! Description<br />
|-<br />
| 0x1EC1xn00<br />
| KRN_WIDTH<br />
| 4<br />
| Kernel width - 1 is written here, 1 <= width <= 8<br />
<br />
This decides how many pixels are written each batch.<br />
|-<br />
| 0x1EC1xn04<br />
| KRN_PATTERN_BITS<br />
| 4<br />
| If the corresponding bit for the current batch iteration index is set then a new pixel is read.<br />
<br />
The amount of set bits determine how many pixels are read each batch. Any bit indexes past KRN_WIDTH are ignored.<br />
<br />
This value is 8 bits, but it has to be written with a 32bit write.<br />
|-<br />
| 0x1EC1xn40<br />
| KRN_MTX<br />
| 0xC0<br />
| int kerneldata[6][8]; - matrix data is written here, height is always 6<br />
|-<br />
|}<br />
<br />
=Descriptions=<br />
<br />
==MTX_CNT==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Enable bit (?)<br />
|-<br />
| 1<br />
| Enable vertical matrix<br />
|-<br />
| 2<br />
| Enable horizontal matrix<br />
|-<br />
| 4<br />
| ???<br />
|-<br />
| 5<br />
| ???<br />
|-<br />
| 8-9<br />
| Input pixel mode? 0 = 4byte color, 1 = 3byte color, 2 = 2byte color, 3 = 2byte color<br />
|-<br />
| 10-11<br />
| Output framebuffer rotation: 0 = normal, 1 = 90° CW (right), 2 = 180° CW (upside down, not mirrored), 3 = 270° CW (left)<br />
|-<br />
| 12<br />
| Output tiling for use with the GPU. When set, the output width and height must be a multiple of 8.<br />
|-<br />
| 15<br />
| Interrupt enable (?)<br />
|-<br />
| 16<br />
| Data still available flag (?)<br />
|-<br />
|}<br />
<br />
==MTX_SIZE==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0-8<br />
| Output framebuffer width - 1 is written here, 1 <= width <= 512<br />
|-<br />
| 16-25<br />
| Output framebuffer height - 1 is written here, 1 <= height <= 512<br />
|-<br />
|}<br />
<br />
==MTX_ACK==<br />
<br />
Reading this register will return pending interrupts.<br />
Writing this register will acknowledge the pending interrupt.<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Interrupt 0<br />
|-<br />
| 1<br />
| Interrupt 1<br />
|-<br />
| 2<br />
| Interrupt 2<br />
|-<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=I2C_Registers&diff=21020I2C Registers2019-07-14T17:58:40Z<p>MarcusD: fix interrupt bit confusion</p>
<hr />
<div>= Registers =<br />
{| class="wikitable" border="1"<br />
! Old3DS<br />
! Name<br />
! Address<br />
! Width<br />
! Used by<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_DATA<br />
| 0x10161000<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C1_CNT]]<br />
| 0x10161001<br />
| 1<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_CNTEX<br />
| 0x10161002<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C1_SCL<br />
| 0x10161004<br />
| 2<br />
| I2C bus 1 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_DATA<br />
| 0x10144000<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C2_CNT]]<br />
| 0x10144001<br />
| 1<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_CNTEX<br />
| 0x10144002<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C2_SCL<br />
| 0x10144004<br />
| 2<br />
| I2C bus 2 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_DATA<br />
| 0x10148000<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| [[#I2C_CNT|I2C3_CNT]]<br />
| 0x10148001<br />
| 1<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_CNTEX<br />
| 0x10148002<br />
| 2<br />
| I2C bus 3 devices<br />
|-<br />
| style="background: green" | Yes<br />
| I2C3_SCL<br />
| 0x10148004<br />
| 2<br />
| I2C bus 3 devices<br />
|}<br />
<br />
== I2C_CNT ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0<br />
| Stop (0=No, 1=Stop/last byte)<br />
|-<br />
| 1<br />
| Start (0=No, 1=Start/first byte)<br />
|-<br />
| 2<br />
| Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)<br />
|-<br />
| 4<br />
| Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)<br />
|-<br />
| 5<br />
| Data Direction (0=Write, 1=Read)<br />
|-<br />
| 6<br />
| Interrupt Enable (0=Disable, 1=Enable)<br />
|-<br />
| 7<br />
| Start/busy (0=Ready, 1=Start/busy)<br />
|}<br />
<br />
== I2C_CNTEX ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-1<br />
| ? Set to 2 normally.<br />
|}<br />
<br />
== I2C_SCL ==<br />
{| class="wikitable" border="1"<br />
! BIT<br />
! DESCRIPTION<br />
|-<br />
| 0-5<br />
| ?<br />
|-<br />
| 8-12<br />
| ? Set to 5 normally.<br />
|}<br />
<br />
= I2C Devices =<br />
{| class="wikitable" border="1"<br />
! [[I2C_Registers|Device id]]<br />
! Device bus id<br />
! Device Write Address<br />
! Accessible via I2C [[I2C_Services|service]]<br />
! Device description<br />
|-<br />
| 0<br />
| 1<br />
| 0x4a<br />
| "i2c::MCU"<br />
| Power management?(same device addr as the DSi power-management)<br />
|-<br />
| 1<br />
| 1<br />
| 0x7a<br />
| "i2c::CAM"<br />
| Camera0?(same dev-addr as DSi cam0)<br />
|-<br />
| 2<br />
| 1<br />
| 0x78<br />
| "i2c::CAM"<br />
| Camera1?(same dev-addr as DSi cam1)<br />
|-<br />
| 3<br />
| 2<br />
| 0x4a<br />
| "i2c::MCU"<br />
| MCU<br />
|-<br />
| 4<br />
| 2<br />
| 0x78<br />
| "i2c::CAM"<br />
| ?<br />
|-<br />
| 5<br />
| 2<br />
| 0x2c<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 6<br />
| 2<br />
| 0x2e<br />
| "i2c::LCD"<br />
| ?<br />
|-<br />
| 7<br />
| 2<br />
| 0x40<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 8<br />
| 2<br />
| 0x44<br />
| "i2c::DEB"<br />
| ?<br />
|-<br />
| 9<br />
| 3<br />
| 0xa6<br />
| "i2c::HID"<br />
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].<br />
|-<br />
| 10<br />
| 3<br />
| 0xd0<br />
| "i2c::HID"<br />
| Gyroscope<br />
|-<br />
| 11<br />
| 3<br />
| 0xd2<br />
| "i2c::HID"<br />
| ?<br />
|-<br />
| 12<br />
| 3<br />
| 0xa4<br />
| "i2c::HID"<br />
| DebugPad<br />
|-<br />
| 13<br />
| 3<br />
| 0x9a<br />
| "i2c::IR"<br />
| IR<br />
|-<br />
| 14<br />
| 3<br />
| 0xa0<br />
| "i2c::EEP"<br />
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])<br />
|-<br />
| 15<br />
| 2<br />
| 0xee<br />
| "i2c::NFC"<br />
| New3DS-only [[NFC_Services|NFC]]<br />
|-<br />
| 16<br />
| 1<br />
| 0x40<br />
| "i2c::QTM"<br />
| New3DS-only [[QTM_Services|QTM]]<br />
|-<br />
| 17<br />
| 3<br />
| 0x54<br />
| "i2c::IR"<br />
| Used by IR-module starting with [[8.0.0-18]], for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on [[8.0.0-18]](actual support was later added in New3DS i2c module).<br />
|}<br />
<br />
'''Notice''': 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 >> 1.<br />
<br />
== Device 3 ==<br />
ro = read-only (writing is no-op)<br />
rw = read-write<br />
wo = write-only (reading will yield 00, FF, or unpredictable data)<br />
<br />
d* = dynamic register (explaination below this table)<br />
s* = shared register (explaination below this table)<br />
ds = dynamic shared (explaination below this table)<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! INFO<br />
! DESCRIPTION <br />
|-<br />
| 0x00<br />
| s<br />
| ro<br />
| Version high<br />
|-<br />
| 0x01<br />
| s<br />
| ro<br />
| Version low<br />
|-<br />
| 0x02<br />
| d<br />
| rw<br />
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog<br />
bit0: RTC clock value got reset to defaults<br />
bit1: Watchdog reset happened<br />
|-<br />
| 0x03<br />
| ds<br />
| rw<br />
| Top screen Vcom<br />
|-<br />
| 0x04<br />
| ds<br />
| rw<br />
| Bottom screen Vcom<br />
|-<br />
| 0x05<br />
- 0x07<br />
| s<br />
| rw<br />
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.<br />
|-<br />
| 0x08<br />
| s<br />
| ro<br />
| Raw 3D slider position<br />
|-<br />
| 0x09<br />
| s<br />
| ro<br />
| Volume slider state (0x00 - 0x3F)<br />
This is the same value returned by [[MCUHWC:GetSoundVolume|MCUHWC:GetSoundVolume]]<br />
|-<br />
| 0x0A<br />
| s<br />
| ro<br />
| ? (seems to be power management related?)<br />
|-<br />
| 0x0B<br />
| s<br />
| ro<br />
| Battery percentage<br />
|-<br />
| 0x0C<br />
| s<br />
| ro<br />
| ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)<br />
|-<br />
| 0x0D<br />
| s<br />
| ro<br />
| System voltage<br />
|-<br />
| 0x0E<br />
| s<br />
| ro<br />
| ?<br />
|-<br />
| 0x0F<br />
| s<br />
| ro<br />
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of these are read via [[MCU_Services|mcu::RTC]]: bit4 = BatteryChargeState. bit3 = AdapterState. bit1 = ShellState.<br />
|-<br />
| 0x10<br />
- 0x13<br />
| s<br />
| ro<br />
| Received interrupt bitmask, see register 0x18 for possible values <br />
If no interrupt was received this register is 0<br />
|-<br />
| 0x14<br />
| s<br />
| ro<br />
| Unused and unwritable byte :(<br />
|-<br />
| 0x15<br />
- 0x17<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x18<br />
- 0x1B<br />
| s<br />
| rw<br />
| Interrupt mask for register 0x10 (0=enabled,1=disabled)<br />
bit00: Power button press (for 27 "ticks")<br />
bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)<br />
bit02: HOME button press (for 5 "ticks")<br />
bit03: HOME button release<br />
bit04: WiFi switch button<br />
bit05: Shell close<br />
bit06: Shell open<br />
bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)<br />
bit08: Charger removed<br />
bit09: Charger plugged in<br />
bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)<br />
bit11: ??? (accelerometer related)<br />
bit12: HID update<br />
bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)<br />
bit14: Battery stopped charging (independent of charger state)<br />
bit15: Battery started charging<br />
Nonmaskable(?) interrupts<br />
bit16: ???<br />
bit17: ??? (opposite even for bit16)<br />
bit22: Volume slider position change<br />
bit23: ??? Register 0x0E update<br />
bit24: ??? (the off event for below bit)<br />
bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)<br />
bit26: ??? (???)<br />
bit27: ??? (???)<br />
bit28: ??? (???)<br />
bit29: ??? backlight on?<br />
bit30: bit set by mcu sysmodule<br />
bit31: bit set by mcu sysmodule<br />
|-<br />
| 0x1C<br />
- 0x1F<br />
| s<br />
| rw<br />
| Unused and unreferenced free RAM! Good for userdata.<br />
|-<br />
| 0x20<br />
| d<br />
| wo<br />
| System power control:<br />
bit0: power off<br />
bit1: reboot (unused?)<br />
bit2: reboot (used by mcu sysmodule and LgyBg)<br />
bit3: used by LgyBg to power off, causes hangs in 3DS-mode<br />
bit4: an mcu::RTC command uses this, seems to do something with the watchdog<br />
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. <br />
|-<br />
| 0x21<br />
| d<br />
| wo<br />
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)<br />
|-<br />
| 0x22<br />
| d<br />
| wo<br />
| Used to set LCD states<br />
bit0: don't push to LCDs<br />
bit1: push to LCDs<br />
bit2: bottom screen backlight off<br />
bit3: bottom screen backlight on<br />
bit4: top screen backlight off<br />
bit5: top screen backlight on<br />
<br />
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.<br />
The rest of the bits are masked away.<br />
|-<br />
| 0x23<br />
| ??<br />
| wo<br />
| ??? Seems to be stubbed, just returns the written value from the write handler function.<br />
|-<br />
| 0x24<br />
| s<br />
| rw<br />
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.<br />
|-<br />
| 0x25<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x26<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x27<br />
| sd<br />
| rw<br />
| Raw volume slider state<br />
|-<br />
| 0x28<br />
| s<br />
| rw<br />
| Brightness of the WiFi/Power LED<br />
|-<br />
| 0x29<br />
| sd(5)<br />
| ??<br />
| Power mode indicator state (read-write)<br />
1 = forced default blue<br />
2 = sleep mode animation<br />
3 = "power off" mode<br />
4 = disable blue power LED and turn on red power LED<br />
5 = disable red power LED and turn on blue power LED<br />
6 = animate blue power LED off and flash red power LED<br />
anything else = automatic mode<br />
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)<br />
|-<br />
| 0x2A<br />
| s<br />
| rw<br />
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide<br />
|-<br />
| 0x2B<br />
| s<br />
| rw<br />
| Camera LED state, 4bits wide,<br />
0, 3, 6-0xF = off<br />
1 = slowly blinking<br />
2 = constantly on<br />
3 = "TWL" mode<br />
4 = flash once<br />
5 = delay before changing to 2<br />
|-<br />
| 0x2C<br />
| s<br />
| rw<br />
| 3D LED state, 4 bits wide<br />
|-<br />
| 0x2D<br />
| 0x64<br />
| wo<br />
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED (see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written. It'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's considered write-only. Writing past the size of this register seems to do nothing.<br />
|-<br />
| 0x2E<br />
| s<br />
| ro<br />
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)<br />
|-<br />
| 0x2F<br />
| s<br />
| wo?<br />
| ??? The write function for this register is stubbed.<br />
|-<br />
| 0x30<br />
- 0x36<br />
| ds<br />
| rw<br />
| 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 & 0xF) + (10 * (byte >> 4)).<br />
byte 0: seconds<br />
byte 1: minutes<br />
byte 2: hours<br />
byte 3: current week (unused)<br />
byte 4: days<br />
byte 5: months<br />
byte 6: years<br />
|-<br />
| 0x37<br />
| s<br />
| rw<br />
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)<br />
|-<br />
| 0x38<br />
- 0x3C<br />
| s<br />
| rw<br />
| RTC alarm registers<br />
byte 0: minutes<br />
byte 1: hours<br />
byte 2: day<br />
byte 3: month<br />
byte 4: year<br />
|-<br />
| 0x3B<br />
| s<br />
| rw<br />
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.<br />
|-<br />
| 0x3D<br />
0x3E<br />
| ds<br />
| ro<br />
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)<br />
Only reading 0x3D will update the in-RAM value<br />
|-<br />
| 0x3F<br />
| s<br />
| wo<br />
| 2 bits<br />
bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)<br />
bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output<br />
|-<br />
| 0x40<br />
| s<br />
| rw<br />
| Pedometer state (?)<br />
|-<br />
| 0x41<br />
| s<br />
| rw<br />
| Index selector for register 0x44<br />
|-<br />
| 0x42<br />
| s<br />
| rw<br />
| Unused?<br />
|-<br />
| 0x43<br />
| s<br />
| rw<br />
| Unused???, accelometer related<br />
|-<br />
| 0x44<br />
| s<br />
| rw<br />
| ???, accelometer related<br />
|-<br />
| 0x45<br />
- 0x4A<br />
| s<br />
| ro<br />
| Gyroscope 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short<br />
{| class="wikitable" border="1"<br />
! AXIS<br />
! V=0x00<br />
! V=0x40<br />
! V=0xC0 <br />
|-<br />
| Y (=roll)<br />
| held vertically<br />
| vertical right side<br />
| vertical left side<br />
|-<br />
| Z? (=yaw)<br />
| ???<br />
| ???<br />
| ???<br />
|-<br />
| X? (=pitch)<br />
| held vertically<br />
| ???<br />
| ???<br />
|}<br />
|-<br />
| 0x4B<br />
| s<br />
| rw<br />
| PedometerStepCount (for the current day)<br />
|-<br />
| 0x4C<br />
0x4D<br />
| ??<br />
| ??<br />
| ??<br />
|-<br />
| 0x4E<br />
| d<br />
| rw<br />
| ??? this = (0xFFE9E & 1) ? 0x10 : 0<br />
|-<br />
| 0x4F<br />
| d(6)<br />
| ro<br />
| <br />
|-<br />
| 0x50<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x51<br />
| s<br />
| rw<br />
| ???<br />
|-<br />
| 0x52<br />
- 0x57<br />
| s<br />
| rw<br />
| ?<br />
|-<br />
| 0x58<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)<br />
|-<br />
| 0x59<br />
| s<br />
| rw<br />
| Register-mapped ADC register<br />
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)<br />
|-<br />
| 0x5A<br />
| s<br />
| ro/rw<br />
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.<br />
|-<br />
| 0x5B<br />
- 0x5F<br />
| s<br />
| - <br />
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x60<br />
| ds<br />
| rw<br />
| Looping queue register<br />
Writing to first byte resets the queue position to the nth element<br />
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)<br />
|-<br />
| 0x61<br />
| ds(0x100)<br />
| rw<br />
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.<br />
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.<br />
|-<br />
| 0x62 - 0x7E<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|-<br />
| 0x7F<br />
| d(9-0x13)<br />
| ro<br />
| Various system state information.<br />
byte 0x06: battery related? (seems to decrease while charging and increase while discharging)<br />
byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)<br />
byte 0x0A: power LED related? 0 is off, 1 is red<br />
byte 0x0D: RGB LED red intensity<br />
byte 0x0E: RGB LED green intensity<br />
byte 0x0F: RGB LED blue intensity<br />
byte 0x11: WiFi LED brightness<br />
byte 0x12: raw button states?<br />
bit0: unset while power button is held<br />
bit1: unset while home button is held<br />
bit2: unset while Wifi slider is held<br />
bit5: unset while the charging LED is active<br />
bit6: unset while charger is plugged in<br />
<br />
this byte is reset to 0 before an svcBreak takes effect<br />
<br />
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.<br />
|-<br />
| 0x80<br />
- 0xFF<br />
| s<br />
| -<br />
| These registers don't exist, writing is no-op, reading will yield FFs.<br />
|}<br />
<br />
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.<br />
<br />
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.<br />
<br />
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.<br />
<br />
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.<br />
<br />
== Device 5 & 6 ==<br />
LCD controllers for main/sub displays, most likely.<br />
<br />
{| class="wikitable" border="1"<br />
! Register<br />
! Width<br />
! Name<br />
! Description<br />
|-<br />
| 0x1<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x11<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x40<br />
| 8<br />
| CMD_IN/CMD_RESULT1<br />
| Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.<br />
|-<br />
| 0x41<br />
| 8<br />
| CMD_RESULT0<br />
| Read result <br />
|-<br />
| 0x50<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0x60<br />
| 8<br />
| ?<br />
| <br />
|-<br />
| 0xFE<br />
| 8<br />
| ?<br />
| <br />
|}<br />
<br />
== Device 10 ==<br />
See the datasheet linked to on the [[Hardware]] page for reference.<br />
<br />
== Device 12 ==<br />
{| class="wikitable" border="1"<br />
! REGISTER<br />
! WIDTH<br />
! DESCRIPTION <br />
|-<br />
| 0x0<br />
| 21<br />
| DebugPad state.<br />
|}<br />
<br />
This is the DebugPad device, see [[HID_Shared_Memory|here]].<br />
<br />
== Device 13 ==<br />
{| class="wikitable" border="1"<br />
! Raw I2C register address<br />
! Internal register address<br />
! Width<br />
! Description <br />
|-<br />
| 0x0<br />
| 0x0<br />
| 0x40<br />
| RHR / THR (data receive/send FIFO)<br />
|-<br />
| 0x8<br />
| 0x1<br />
| 0x1<br />
| IER<br />
|-<br />
| 0x10<br />
| 0x2<br />
| 0x1<br />
| FCR/IIR<br />
|-<br />
| 0x18<br />
| 0x3<br />
| 0x1<br />
| LCR<br />
|-<br />
| 0x20<br />
| 0x4<br />
| 0x1<br />
| MCR<br />
|-<br />
| 0x28<br />
| 0x5<br />
| 0x1<br />
| LSR<br />
|-<br />
| 0x30<br />
| 0x6<br />
| 0x1<br />
| MSR/TCR<br />
|-<br />
| 0x38<br />
| 0x7<br />
| 0x1<br />
| SPR/TLR<br />
|-<br />
| 0x40<br />
| 0x8<br />
| 0x1<br />
| TXLVL<br />
|-<br />
| 0x48<br />
| 0x9<br />
| 0x1<br />
| RXLVL<br />
|-<br />
| 0x50<br />
| 0xA<br />
| 0x1<br />
| IODir<br />
|-<br />
| 0x58<br />
| 0xB<br />
| 0x1<br />
| IOState<br />
|-<br />
| 0x60<br />
| 0xC<br />
| 0x1<br />
| IoIntEna<br />
|-<br />
| 0x68<br />
| 0xD<br />
| 0x1<br />
| reserved<br />
|-<br />
| 0x70<br />
| 0xE<br />
| 0x1<br />
| IOControl<br />
|-<br />
| 0x78<br />
| 0xF<br />
| 0x1<br />
| EFCR<br />
|}<br />
<br />
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: "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."<br />
<br />
== Device 14 ==<br />
<br />
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.<br />
<br />
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.<br />
<br />
== Device 15 ==<br />
This the New3DS [[NFC_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|commands]].<br />
<br />
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.<br />
<br />
Command request / response structure:<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Size<br />
! Description<br />
|-<br />
| 0x0<br />
| 0x1<br />
| Normally 0x10?<br />
|-<br />
| 0x1<br />
| 0x1<br />
| Command source / destination.<br />
|-<br />
| 0x2<br />
| 0x1<br />
| CmdID<br />
|-<br />
| 0x3<br />
| 0x1<br />
| Payload size.<br />
|}<br />
<br />
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.<br />
<br />
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.<br />
<br />
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:<br />
000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33 Dec 22 201214:53 <br />
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39 :50...F..y .2079<br />
000020: 31 42 35 1B5<br />
<br />
Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).<br />
<br />
=== NFC controller commands ===<br />
{| class="wikitable" border="1"<br />
! CmdRequest[1]<br />
! CmdID<br />
! Payload data for parameters<br />
! Description<br />
|-<br />
| 0x2E<br />
| 0x2F<br />
| Firmware image for this chunk, size varies.<br />
| 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.<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=MTX_Registers&diff=21017MTX Registers2019-07-07T01:39:29Z<p>MarcusD: /* MTX_CNT */</p>
<hr />
<div>These registers are responsible for controlling how framebuffer data can be DMA'd from the DS GPU, and also for configuring the upscaling matrix.<br />
<br />
=Registers=<br />
<br />
The physical address can be calculated by subtracting 0xEB00000 from the virtual address.<br />
<br />
==Control==<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
|-<br />
| 0x1EC1x000<br />
| [[#MTX_CNT|MTX_CNT]]<br />
| 4<br />
|-<br />
| 0x1EC1x004<br />
| [[#MTX_SIZE|MTX_SIZE]]<br />
| 4<br />
|-<br />
| 0x1EC1x008<br />
| [[#MTX_ACK|MTX_ACK]]<br />
| 4<br />
|-<br />
| 0x1EC1x00C<br />
| ???<br />
| 4<br />
|-<br />
| 0x1EC1x020<br />
| ???<br />
| 4<br />
|-<br />
|}<br />
<br />
==Matrix unit==<br />
<br />
There are two matrix units, one at +0x200, and the other one at +0x300, possibly one for each axis (X and Y)<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
! Description<br />
|-<br />
| 0x1EC1xn00<br />
| KRN_WIDTH<br />
| 4<br />
| Kernel width - 1 is written here, 1 <= width <= 8<br />
<br />
This decides how many pixels are written each batch.<br />
|-<br />
| 0x1EC1xn04<br />
| KRN_PATTERN_BITS<br />
| 4<br />
| If the corresponding bit for the current batch iteration index is set then a new pixel is read.<br />
<br />
The amount of set bits determine how many pixels are read each batch. Any bit indexes past KRN_WIDTH are ignored.<br />
<br />
This value is 8 bits, but it has to be written with a 32bit write.<br />
|-<br />
| 0x1EC1xn40<br />
| KRN_MTX<br />
| 0xC0<br />
| int kerneldata[6][8]; - matrix data is written here, height is always 6<br />
|-<br />
|}<br />
<br />
=Descriptions=<br />
<br />
==MTX_CNT==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Enable bit<br />
|-<br />
| 1<br />
| ??? set after init sequence<br />
|-<br />
| 2<br />
| ??? set after init sequence<br />
|-<br />
| 3<br />
| ???<br />
|-<br />
| 4<br />
| ???<br />
|-<br />
| 8-9<br />
| Input pixel mode? 0 = 4byte color, 1 = 3byte color, 2 = 2byte color, 3 = 2byte color<br />
|-<br />
| 10-11<br />
| Output pixel mode? Same enum as above.<br />
|-<br />
| 12<br />
| ??? initialized to 0, but if set then requires the output width and height to be multiples of 8<br />
|-<br />
| 15<br />
| ??? set after init sequence<br />
|-<br />
| 16<br />
| ??? not yet inited bit ???<br />
|-<br />
|}<br />
<br />
==MTX_SIZE==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0-8<br />
| Output framebuffer width - 1 is written here<br />
|-<br />
| 16-25<br />
| Output framebuffer height - 1 is written here<br />
|-<br />
|}<br />
<br />
==MTX_ACK==<br />
<br />
Reading this register will return pending interrupts.<br />
Writing this register will acknowledge the pending interrupt.<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Interrupt 0<br />
|-<br />
| 1<br />
| Interrupt 1<br />
|-<br />
| 2<br />
| Interrupt 2<br />
|-<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=MTX_Registers&diff=21016MTX Registers2019-07-06T23:43:32Z<p>MarcusD: /* Matrix unit */</p>
<hr />
<div>These registers are responsible for controlling how framebuffer data can be DMA'd from the DS GPU, and also for configuring the upscaling matrix.<br />
<br />
=Registers=<br />
<br />
The physical address can be calculated by subtracting 0xEB00000 from the virtual address.<br />
<br />
==Control==<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
|-<br />
| 0x1EC1x000<br />
| [[#MTX_CNT|MTX_CNT]]<br />
| 4<br />
|-<br />
| 0x1EC1x004<br />
| [[#MTX_SIZE|MTX_SIZE]]<br />
| 4<br />
|-<br />
| 0x1EC1x008<br />
| [[#MTX_ACK|MTX_ACK]]<br />
| 4<br />
|-<br />
| 0x1EC1x00C<br />
| ???<br />
| 4<br />
|-<br />
| 0x1EC1x020<br />
| ???<br />
| 4<br />
|-<br />
|}<br />
<br />
==Matrix unit==<br />
<br />
There are two matrix units, one at +0x200, and the other one at +0x300, possibly one for each axis (X and Y)<br />
<br />
{| class="wikitable" border="1"<br />
! VAddress<br />
! Name<br />
! Width<br />
! Description<br />
|-<br />
| 0x1EC1xn00<br />
| KRN_WIDTH<br />
| 4<br />
| Kernel width - 1 is written here, 1 <= width <= 8<br />
<br />
This decides how many pixels are written each batch.<br />
|-<br />
| 0x1EC1xn04<br />
| KRN_PATTERN_BITS<br />
| 4<br />
| If the corresponding bit for the current batch iteration index is set then a new pixel is read.<br />
<br />
The amount of set bits determine how many pixels are read each batch. Any bit indexes past KRN_WIDTH are ignored.<br />
<br />
This value is 8 bits, but it has to be written with a 32bit write.<br />
|-<br />
| 0x1EC1xn40<br />
| KRN_MTX<br />
| 0xC0<br />
| int kerneldata[6][8]; - matrix data is written here, height is always 6<br />
|-<br />
|}<br />
<br />
=Descriptions=<br />
<br />
==MTX_CNT==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Enable bit<br />
|-<br />
| 1<br />
| ??? set after init sequence<br />
|-<br />
| 2<br />
| ??? set after init sequence<br />
|-<br />
| 8-9<br />
| Mode? 0 = 4byte color, 1 = 3byte color, 2 = 2byte color, 3 = 2byte color<br />
|-<br />
| 10-11<br />
| Another mode? Input mode? Initialized to 0<br />
|-<br />
| 12<br />
| ??? initialized to 0<br />
|-<br />
| 15<br />
| ??? set after init sequence<br />
|-<br />
| 16<br />
| ??? not yet inited bit ???<br />
|-<br />
|}<br />
<br />
==MTX_SIZE==<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0-8<br />
| Output framebuffer width - 1 is written here<br />
|-<br />
| 16-25<br />
| Output framebuffer height - 1 is written here<br />
|-<br />
|}<br />
<br />
==MTX_ACK==<br />
<br />
Reading this register will return pending interrupts.<br />
Writing this register will acknowledge the pending interrupt.<br />
<br />
{| class="wikitable" border="1"<br />
! Bit(s)<br />
! Description<br />
|-<br />
| 0<br />
| Interrupt 0<br />
|-<br />
| 1<br />
| Interrupt 1<br />
|-<br />
| 2<br />
| Interrupt 2<br />
|-<br />
|}</div>MarcusDhttps://www.3dbrew.org/w/index.php?title=GPU/External_Registers&diff=21015GPU/External Registers2019-07-05T13:09:42Z<p>MarcusD: /* LCD Source Framebuffer Setup */</p>
<hr />
<div>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]].<br />
<br />
== Map ==<br />
Address mappings for the external registers. GSPGPU:WriteHWRegs takes these addresses relative to 0x1EB00000. <br />
{| class="wikitable" border="1"<br />
! User VA<br />
! PA<br />
! Length<br />
! Name<br />
! Comments<br />
|-<br />
| 0x1EF00004<br />
| 0x10400004<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00010<br />
| 0x10400010<br />
| 16<br />
| [[#Memory Fill|Memory Fill1]] "PSC0"<br />
| GX command 2<br />
|-<br />
| 0x1EF00020<br />
| 0x10400020<br />
| 16<br />
| [[#Memory Fill|Memory Fill2]] "PSC1"<br />
| GX command 2<br />
|-<br />
| 0x1EF00030<br />
| 0x10400030<br />
| 4<br />
| ?<br />
|<br />
|-<br />
| 0x1EF00034<br />
| 0x10400034<br />
| 4<br />
| GPU Busy<br />
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.<br />
|-<br />
| 0x1EF00050<br />
| 0x10400050<br />
| 4<br />
| ?<br />
| Writes 0x22221200 on GPU init.<br />
|-<br />
| 0x1EF00054<br />
| 0x10400054<br />
| 4<br />
| ?<br />
| Writes 0xFF2 on GPU init.<br />
|-<br />
| 0x1EF000C0<br />
| 0x104000C0<br />
| 4<br />
| Backlight control<br />
| Writes 0x0 to allow backlights to turn off, 0x20000000 to force them always on.<br />
|-<br />
| 0x1EF00400<br />
| 0x10400400<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC0" (top screen)<br />
|<br />
|-<br />
| 0x1EF00500<br />
| 0x10400500<br />
| 0x100<br />
| [[#LCD Source Framebuffer Setup|Framebuffer Setup]] "PDC1" (bottom)<br />
|<br />
|-<br />
| 0x1EF00C00<br />
| 0x10400C00<br />
| ?<br />
| [[#Transfer_Engine|Transfer Engine]] "DMA"<br />
|<br />
|-<br />
|colspan="5"| 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)<br />
|-<br />
| 0x1EF01000<br />
| 0x10401000<br />
| 0x4<br />
| ?<br />
| Writes 0 on GPU init and before the Command List is used<br />
|-<br />
| 0x1EF01080<br />
| 0x10401080<br />
| 0x4<br />
| ?<br />
| Writes 0x12345678 on GPU init.<br />
|-<br />
| 0x1EF010C0<br />
| 0x104010C0<br />
| 0x4<br />
| ?<br />
| Writes 0xFFFFFFF0 on GPU init.<br />
|-<br />
| 0x1EF010D0<br />
| 0x104010D0<br />
| 0x4<br />
| ?<br />
| Writes 1 on GPU init.<br />
|-<br />
| 0x1EF014??<br />
| 0x104014??<br />
| 0x14<br />
| "PPF" ?<br />
|<br />
|-<br />
| 0x1EF018E0<br />
| 0x104018E0<br />
| 0x14<br />
| [[#Command_List|Command List]] "P3D"<br />
|<br />
|}<br />
<br />
== Memory Fill ==<br />
{| class="wikitable" border="1"<br />
! User VA<br />
! Description<br />
|-<br />
| 0x1EF000X0<br />
| Buffer start physaddr >> 3<br />
|-<br />
| 0x1EF000X4<br />
| Buffer end physaddr >> 3<br />
|-<br />
| 0x1EF000X8<br />
| Fill value<br />
|-<br />
| 0x1EF000XC<br />
| Control. bit0: start/busy, bit1: finished, bit8-9: fill-width (0=16bit, 1=3=24bit, 2=32bit)<br />
|}<br />
<br />
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.<br />
<br />
These registers are used by [[GSP Shared Memory#GX SetMemoryFill|GX SetMemoryFill]].<br />
<br />
== LCD Source Framebuffer Setup ==<br />
<br />
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.<br />
<br />
{| class="wikitable" border="1"<br />
! Offset<br />
! Name<br />
! Comments<br />
|-<br />
| 0x00<br />
| Pixel clock<br />
| Higher values are slower, 12bits.<br />
<br />
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).<br />
|-<br />
| 0x04<br />
| HBlank timer(?)<br />
| Seems to determine the horizontal blanking interval.<br />
<br />
<br />
Setting this to lower than <code>HTotal - HDisp</code> will make the screen not catch up with the scanlines, some will be skipped, some will be misaligned.<br />
<br />
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.<br />
<br />
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.<br />
|-<br />
| 0x08<br />
| ?<br />
| must be >= REG#0x00<br />
|-<br />
| 0x0C<br />
| ?<br />
| must be >= REG#0x08<br />
|-<br />
| 0x10<br />
| Window X start (LgyFb)<br />
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.<br />
<br />
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.<br />
|-<br />
| 0x14<br />
| Window X end (LgyFb)<br />
| Window X start + window width - 1 is written here to set the end display scan pixel offset.<br />
<br />
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 "pixels". 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.<br />
|-<br />
| 0x18<br />
| Window Y start (LgyFb)<br />
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.<br />
|-<br />
| 0x20<br />
| Low: Window Y end (LgyFb)<br />
High: ???<br />
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.<br />
<br />
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.<br />
<br />
<br />
??? 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.<br />
|-<br />
| 0x24<br />
| Low: ???<br />
High: ???<br />
| The low 12bit halfword seems to affect:<br />
<br />
- the total amount of scanlines displayed<br />
<br />
- vertical pixel data offset if the GPU can't VSync properly<br />
<br />
- VSync length<br />
|-<br />
| 0x28<br />
| VBlank timer(?)<br />
| Seems to determine the vertical blanking interval.<br />
<br />
<br />
Setting this to lower than <code>VTotal - VDisp</code> will cut off the top <code>VTotal - VDisp - thisvalue</code> lines.<br />
<br />
Setting this to higher than <code>VTotal - VDisp</code> will make the image be pushed downwards with the overscan color visible.<br />
<br />
Setting this to higher than <code>HTotal</code> 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 <code>thisvalue + somevalue - HTotal</code> lines into the "global" pixel buffer.<br />
|-<br />
| 0x30<br />
| VTotal<br />
| 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.<br />
|-<br />
| 0x34<br />
| VDisp(?)<br />
| 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.<br />
|-<br />
| 0x38<br />
| Vertical data offset(?)<br />
| ??? 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.<br />
|-<br />
| 0x44<br />
| ???<br />
| similar functionality to 0x10<br />
|-<br />
| 0x48<br />
| ???<br />
| bit0 seems to disable HSync, bit8 seems to disable VSync, rest of the bits aren't writable.<br />
|-<br />
| 0x4C<br />
| Overscan filler color<br />
| <br />
|-<br />
| 0x50<br />
| Horizontal position counter<br />
| read-only<br />
|-<br />
| 0x54<br />
| Horizontal scanline (HBlank) counter<br />
| read-only<br />
|-<br />
| 0x5C<br />
| ???<br />
| low u16: framebuffer width<br />
high u16: framebuffer height??? (seems to be unused)<br />
|-<br />
| 0x60<br />
| ???<br />
| low u16: timing data(?)<br />
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)<br />
|-<br />
| 0x64<br />
| ???<br />
| low u16: unknown<br />
high u16: framebuffer total height (amount of scanlines blitted regardless of framebuffer height)<br />
|-<br />
| 0x68<br />
| Framebuffer A first address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x6C<br />
| Framebuffer A second address<br />
| For top screen, this is the left eye 3D framebuffer.<br />
|-<br />
| 0x70<br />
| Framebuffer format<br />
| Bit0-15: framebuffer format, bit16-31: unknown<br />
|-<br />
| 0x78<br />
| Framebuffer select<br />
| Bit0: which framebuffer to display, bit1-7: unknown<br />
|-<br />
| 0x80<br />
| Color lookup table index select<br />
| 8bits, write-only<br />
|-<br />
| 0x84<br />
| Color lookup table indexed element<br />
| Contains the value of the color lookup table indexed by the above register, 24bits, RGB8 (0x00BBGGRR) <br />
Accessing this register will increase the index register by one<br />
|-<br />
| 0x90<br />
| Framebuffer stride<br />
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).<br />
|-<br />
| 0x94<br />
| Framebuffer B first address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|-<br />
| 0x98<br />
| Framebuffer B second address<br />
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.<br />
|}<br />
<br />
=== Framebuffer format ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Bit<br />
! Description<br />
|-<br />
| 2-0<br />
| Color format<br />
|-<br />
| 3<br />
| ?<br />
|-<br />
| 4<br />
| Unused?<br />
|-<br />
| 5<br />
| Enable parallax barrier (i.e. 3D).<br />
|-<br />
| 6<br />
| 1 = main screen, 0 = sub screen. However if bit5 is set, this bit is cleared.<br />
|-<br />
| 7<br />
| ?<br />
|-<br />
| 9-8<br />
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.<br />
|-<br />
| 15-10<br />
| Unused?<br />
|}<br />
<br />
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.<br />
<br />
=== Framebuffer color formats ===<br />
{| class="wikitable" border="1"<br />
|-<br />
! Value<br />
! Description<br />
|-<br />
| 0<br />
| GL_RGBA8_OES<br />
|-<br />
| 1<br />
| GL_RGB8_OES<br />
|-<br />
| 2<br />
| GL_RGB565_OES<br />
|-<br />
| 3<br />
| GL_RGB5_A1_OES<br />
|-<br />
| 4<br />
| GL_RGBA4_OES<br />
|}<br />
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.<br />
<br />
== Transfer Engine ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF00C00<br />
| Input physical address >> 3<br />
|-<br />
| 0x1EF00C04<br />
| Output physical address >> 3<br />
|-<br />
| 0x1EF00C08<br />
| DisplayTransfer output width (bits 0-15) and height (bits 16-31).<br />
|-<br />
| 0x1EF00C0C<br />
| DisplayTransfer input width and height.<br />
|-<br />
| 0x1EF00C10<br />
| Transfer flags. (See below)<br />
|-<br />
| 0x1EF00C14<br />
| GSP module writes value 0 here prior to writing to 0x1EF00C18, for cmd3.<br />
|-<br />
| 0x1EF00C18<br />
| Setting bit0 starts the transfer. Upon completion, bit0 is unset and bit8 is set.<br />
|-<br />
| 0x1EF00C1C<br />
| ?<br />
|-<br />
| 0x1EF00C20<br />
| TextureCopy total amount of data to copy, in bytes.<br />
|-<br />
| 0x1EF00C24<br />
| TextureCopy input line width (bits 0-15) and gap (bits 16-31), in 16 byte units.<br />
|-<br />
| 0x1EF00C28<br />
| TextureCopy output line width and gap.<br />
|}<br />
<br />
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.<br />
<br />
==== Flags Register - 0x1EF00C10 ====<br />
{| class="wikitable" border="1"<br />
! Bit<br />
! Description<br />
|-<br />
| 0<br />
| When set, the framebuffer data is flipped vertically.<br />
|-<br />
| 1<br />
| When set, the input framebuffer is treated as linear and converted to tiled in the output, converts tiled->linear when unset.<br />
|-<br />
| 2<br />
| 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.<br />
|-<br />
| 3<br />
| Uses a TextureCopy mode transfer. See below for details.<br />
|-<br />
| 4<br />
| Not writable<br />
|-<br />
| 5<br />
| Don't perform tiled-linear conversion. Incompatible with bit 1, so only tiled-tiled transfers can be done, not linear-linear.<br />
|-<br />
| 7-6<br />
| Not writable<br />
|-<br />
| 10-8<br />
| Input framebuffer color format, value0 and value1 are the same as the [[GPU Registers#Framebuffer_color_formats|LCD Source Framebuffer Formats]] (usually zero)<br />
|-<br />
| 11<br />
| Not writable<br />
|-<br />
| 14-12<br />
| Output framebuffer color format<br />
|-<br />
| 15<br />
| Not writable<br />
|-<br />
| 16<br />
| 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.<br />
|-<br />
| 17-23<br />
| Not writable<br />
|-<br />
| 24-25<br />
| Scale down the input image using a box filter. 0 = No downscale, 1 = 2x1 downscale. 2 = 2x2 downscale, 3 = invalid<br />
|-<br />
| 31-26<br />
| Not writable<br />
|}<br />
<br />
=== TextureCopy ===<br />
<br />
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 "gap" specified in the input/output dimension register is the number of chunks to skip after each "width" chunks of the input/output, and is NOT counted towards the total size of the transfer.<br />
<br />
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'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.<br />
<br />
Specifying invalid/junk values for the TextureCopy dimensions can result in the GPU hanging while attempting to process this TextureCopy.<br />
<br />
== Command List ==<br />
{| class="wikitable" border="1"<br />
! Register address<br />
! Description<br />
|-<br />
| 0x1EF018E0<br />
| Buffer size in bytes >> 3<br />
|-<br />
| 0x1EF018E8<br />
| Buffer physical address >> 3<br />
|-<br />
| 0x1EF018F0<br />
| Setting bit0 to 1 enables processing GPU command execution. Upon completion, bit0 seems to be reset to 0.<br />
|}<br />
<br />
These 3 registers are used by [[GSP_Shared_Memory|GX command]] 1. This is used for [[GPU/Internal_Registers|GPU commands]].<br />
<br />
== Framebuffers ==<br />
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)<br />
<br />
Both of the 3D screen left/right framebuffers are displayed regardless of the 3D slider's state, however when the 3D slider is set to "off" the 3D effect is disabled. Normally when the 3D slider's state is set to "off" 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.<br />
<br />
==== Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00400 = 0x1C2<br />
* 0x1EF00404 = 0xD1<br />
* 0x1EF00408 = 0x1C1<br />
* 0x1EF0040C = 0x1C1<br />
* 0x1EF00410 = 0<br />
* 0x1EF00414 = 0xCF<br />
* 0x1EF00418 = 0xD1<br />
* 0x1EF0041C = 0x1C501C1<br />
* 0x1EF00420 = 0x10000<br />
* 0x1EF00424 = 0x19D<br />
* 0x1EF00428 = 2<br />
* 0x1EF0042C = 0x1C2<br />
* 0x1EF00430 = 0x1C2<br />
* 0x1EF00434 = 0x1C2<br />
* 0x1EF00438 = 1<br />
* 0x1EF0043C = 2<br />
* 0x1EF00440 = 0x1960192<br />
* 0x1EF00444 = 0<br />
* 0x1EF00448 = 0<br />
* 0x1EF0045C = 0x19000F0<br />
* 0x1EF00460 = 0x1c100d1<br />
* 0x1EF00464 = 0x1920002<br />
* 0x1EF00470 = 0x80340<br />
* 0x1EF0049C = 0<br />
<br />
==== More Init Values from nngxInitialize for Top Screen ====<br />
* 0x1EF00468 = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF0046C = 0x18300000, later changed by GSP module when updating state, framebuffer<br />
* 0x1EF00494 = 0x18300000<br />
* 0x1EF00498 = 0x18300000<br />
* 0x1EF00478 = 1, doesn't stay 1, read as 0<br />
* 0x1EF00474 = 0x10501<br />
<br />
[[Category:GPU]]</div>MarcusD