3DS reverse engineering

Discussion of development of software for any "obsolete" computer or video game system. See the WSdev wiki and ObscureDev wiki for more information on certain platforms.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

Looking through the gpu init log, the writes to nameless/undocumented registers are looking interesting...

Code: Select all

[GPUREG_008C]=00FF0000               ;PICA(008Ch) 10401230h ;...texunit0
[GPUREG_008D]=00000000               ;PICA(008Dh) 10401234h ;...texunit0
[GPUREG_0090]=00000000               ;PICA(0090h) 10401240h ;...always zero? or write-only?
[GPUREG_0097]=00000000               ;PICA(0097h) 1040125Ch ;...always zero? or write-only?
[GPUREG_0098]=00000000               ;PICA(0098h) 10401260h ;...always zero? or write-only?
[GPUREG_011F]=00010140               ;PICA(011Fh) 1040147Ch ;...framebuf
[GPUREG_0045]=00000000               ;PICA(0045h) 10401114h ;...rasterizer
[GPUREG_0046]=00000000               ;PICA(0046h) 10401118h ;...rasterizer
[GPUREG_004C]=00000000               ;PICA(004Ch) 10401130h ;...rasterizer (clear)
[GPUREG_0058]=00000100               ;PICA(0058h) 10401160h ;...rasterizer
[GPUREG_004C]=00000001               ;PICA(004Ch) 10401130h ;...rasterizer (set)
[GPUREG_0060]=00000000,MASK=0000FF00 ;PICA(0060h) 10401180h ;...rasterizer
[GPUREG_0069]=00020000,MASK=FFFF0000 ;PICA(0069h) 104011A4h ;...rasterizer
[GPUREG_01D4]=00000001               ;PICA(01D4h) 10401750h ;...lighting
[GPUREG_00AE]=FEFCF8F0               ;PICA(00AEh) 104012B8h ;...proctex
Using that settings didn't help though... still no triangle displayed : /

These registers are also shown without names in the log, but they do have names on 3dbrew.

Code: Select all

[GPUREG_008E]=00000000               ;PICA(008Eh) 10401238h ;GPUREG_TEXUNIT0_TYPE
[GPUREG_008F]=00000000               ;PICA(008Fh) 1040123Ch ;GPUREG_LIGHTING_ENABLE0
[GPUREG_00A8]=00000009               ;PICA(00A8h) 104012A0h ;GPUREG_TEXUNIT3_PROCTEX0
[GPUREG_00A9]=00000000               ;PICA(00A9h) 104012A4h ;GPUREG_TEXUNIT3_PROCTEX1
[GPUREG_00AA]=00000000               ;PICA(00AAh) 104012A8h ;GPUREG_TEXUNIT3_PROCTEX2
[GPUREG_00AB]=00000000               ;PICA(00ABh) 104012ACh ;GPUREG_TEXUNIT3_PROCTEX3
[GPUREG_00AC]=01C00301               ;PICA(00ACh) 104012B0h ;GPUREG_TEXUNIT3_PROCTEX4
[GPUREG_00AD]=E0C08000               ;PICA(00ADh) 104012B4h ;GPUREG_TEXUNIT3_PROCTEX5
[GPUREG_01A5]=00003C00         ;PICA(0145h+10h*6) 10401xx4h ;GPUREG_LIGHT6_Z
Looks as if they are missing or misdefined in the string database, maybe not so important... unless they are misdefined in other places, too.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

nocash wrote: Sun Apr 26, 2020 7:16 pm Looking through the gpu init log, the writes to nameless/undocumented registers are looking interesting...

Code: Select all

[GPUREG_008C]=00FF0000               ;PICA(008Ch) 10401230h ;...texunit0
[GPUREG_008D]=00000000               ;PICA(008Dh) 10401234h ;...texunit0
[GPUREG_0090]=00000000               ;PICA(0090h) 10401240h ;...always zero? or write-only?
[GPUREG_0097]=00000000               ;PICA(0097h) 1040125Ch ;...always zero? or write-only?
[GPUREG_0098]=00000000               ;PICA(0098h) 10401260h ;...always zero? or write-only?
[GPUREG_011F]=00010140               ;PICA(011Fh) 1040147Ch ;...framebuf
[GPUREG_0045]=00000000               ;PICA(0045h) 10401114h ;...rasterizer
[GPUREG_0046]=00000000               ;PICA(0046h) 10401118h ;...rasterizer
[GPUREG_004C]=00000000               ;PICA(004Ch) 10401130h ;...rasterizer (clear)
[GPUREG_0058]=00000100               ;PICA(0058h) 10401160h ;...rasterizer
[GPUREG_004C]=00000001               ;PICA(004Ch) 10401130h ;...rasterizer (set)
[GPUREG_0060]=00000000,MASK=0000FF00 ;PICA(0060h) 10401180h ;...rasterizer
[GPUREG_0069]=00020000,MASK=FFFF0000 ;PICA(0069h) 104011A4h ;...rasterizer
[GPUREG_01D4]=00000001               ;PICA(01D4h) 10401750h ;...lighting
[GPUREG_00AE]=FEFCF8F0               ;PICA(00AEh) 104012B8h ;...proctex
Using that settings didn't help though... still no triangle displayed : /

These registers are also shown without names in the log, but they do have names on 3dbrew.

Code: Select all

[GPUREG_008E]=00000000               ;PICA(008Eh) 10401238h ;GPUREG_TEXUNIT0_TYPE
[GPUREG_008F]=00000000               ;PICA(008Fh) 1040123Ch ;GPUREG_LIGHTING_ENABLE0
[GPUREG_00A8]=00000009               ;PICA(00A8h) 104012A0h ;GPUREG_TEXUNIT3_PROCTEX0
[GPUREG_00A9]=00000000               ;PICA(00A9h) 104012A4h ;GPUREG_TEXUNIT3_PROCTEX1
[GPUREG_00AA]=00000000               ;PICA(00AAh) 104012A8h ;GPUREG_TEXUNIT3_PROCTEX2
[GPUREG_00AB]=00000000               ;PICA(00ABh) 104012ACh ;GPUREG_TEXUNIT3_PROCTEX3
[GPUREG_00AC]=01C00301               ;PICA(00ACh) 104012B0h ;GPUREG_TEXUNIT3_PROCTEX4
[GPUREG_00AD]=E0C08000               ;PICA(00ADh) 104012B4h ;GPUREG_TEXUNIT3_PROCTEX5
[GPUREG_01A5]=00003C00         ;PICA(0145h+10h*6) 10401xx4h ;GPUREG_LIGHT6_Z
Looks as if they are missing or misdefined in the string database, maybe not so important... unless they are misdefined in other places, too.
In #citra-dev on Freenode a few days ago it was mentioned that the memory mapped internal GPU regs are the "lower half". Not sure what that means exactly but using the command list interface is your best bet and is how i got a triangle drawn. You first need to initialize regs to sane defaults and then use this cmd list i dumped from userland:

Code: Select all

[FRAMEBUFFER_INVALIDATE] = 00000001
[DEPTHBUFFER_LOC] = 0300BB80
-> [COLORBUFFER_LOC] = 03035740
-> [FRAMEBUFFER_DIM] = 0118F0F0
[RENDERBUF_DIM] = 0118F0F0
[DEPTHBUFFER_FORMAT] = 00000003
[COLORBUFFER_FORMAT] = 00000002
[FRAMEBUFFER_BLOCK32] = 00000000
[COLORBUFFER_READ] = 0000000F
-> [COLORBUFFER_WRITE] = 0000000F
-> [DEPTHBUFFER_READ] = 00000003
-> [DEPTHBUFFER_WRITE] = 00000003
[VIEWPORT_WIDTH] = 0045E000
-> [VIEWPORT_INVW] = 38111110
-> [VIEWPORT_HEIGHT] = 00469000
-> [VIEWPORT_INVH] = 3747AE14
[VIEWPORT_XY] = 00000000
[SCISSORTEST_MODE] = 00000000
-> [SCISSORTEST_POS] = 00000000
-> [SCISSORTEST_DIM] = 00000000
[GEOSTAGE_CONFIG] = 00000000, MASK = 0000FFFF
[GEOSTAGE_CONFIG2] = 00000000, MASK = 0000FFFF
[VSH_COM_MODE] = 00000000, MASK = 000000FF
[VSH_CODETRANSFER_INDEX] = 00000000
[VSH_CODETRANSFER_DATA0] = 4E000000
-> [VSH_CODETRANSFER_DATA0] = 4E07F001
-> [VSH_CODETRANSFER_DATA0] = 08020802
-> [VSH_CODETRANSFER_DATA0] = 08021803
-> [VSH_CODETRANSFER_DATA0] = 08022804
-> [VSH_CODETRANSFER_DATA0] = 08023805
-> [VSH_CODETRANSFER_DATA0] = 4C201006
-> [VSH_CODETRANSFER_DATA0] = 88000000
[VSH_CODETRANSFER_END] = 00000001
[VSH_OPDESCS_INDEX] = 00000000
[VSH_OPDESCS_DATA0] = 0000036E
-> [VSH_OPDESCS_DATA0] = 00000AA1
-> [VSH_OPDESCS_DATA0] = 0006C368
-> [VSH_OPDESCS_DATA0] = 0006C364
-> [VSH_OPDESCS_DATA0] = 0006C362
-> [VSH_OPDESCS_DATA0] = 0006C361
-> [VSH_OPDESCS_DATA0] = 0000036F
[VSH_ENTRYPOINT] = 7FFF0000
[VSH_OUTMAP_MASK] = 00000003
[VSH_OUTMAP_TOTAL1] = 00000001
[VSH_OUTMAP_TOTAL2] = 00000001
[PRIMITIVE_CONFIG] = 00000001, MASK = 000000FF
[SH_OUTMAP_TOTAL] = 00000002
-> [SH_OUTMAP_O0] = 03020100
-> [SH_OUTMAP_O1] = 0B0A0908
-> [SH_OUTMAP_O2] = 1F1F1F1F
-> [SH_OUTMAP_O3] = 1F1F1F1F
-> [SH_OUTMAP_O4] = 1F1F1F1F
-> [SH_OUTMAP_O5] = 1F1F1F1F
-> [SH_OUTMAP_O6] = 1F1F1F1F
[SH_OUTATTR_MODE] = 00000000
[SH_OUTATTR_CLOCK] = 00000003
[GEOSTAGE_CONFIG] = 00000000, MASK = FF00FF00
[GSH_MISC0] = 00000000
[GSH_MISC1] = 00000000
[GSH_INPUTBUFFER_CONFIG] = A0000000
[ATTRIBBUFFERS_FORMAT_LOW] = 000000BB
-> [ATTRIBBUFFERS_FORMAT_HIGH] = 1FFC0000
[VSH_INPUTBUFFER_CONFIG] = A0000001, MASK = FF00FFFF
[VSH_NUM_ATTR] = 00000001
[VSH_ATTRIBUTES_PERMUTATION_LOW] = 00000010
-> [VSH_ATTRIBUTES_PERMUTATION_HIGH] = 00000000
[DEPTHMAP_ENABLE] = 00000001
[FACECULLING_CONFIG] = 00000002
[DEPTHMAP_SCALE] = 00BF0000
-> [DEPTHMAP_OFFSET] = 00000000
[FRAGOP_ALPHA_TEST] = 00000010
-> [STENCIL_TEST] = FF000010
-> [STENCIL_OP] = 00000000
-> [DEPTH_COLOR_MASK] = 00001F61
[GAS_DELTAZ_DEPTH] = 02000000, MASK = FF000000
[BLEND_COLOR] = 00000000
[BLEND_FUNC] = 76760000
[LOGIC_OP] = 00000000
[COLOR_OPERATION] = 00E40100, MASK = 00FFFFFF
[FRAGOP_SHADOW] = 80003C00
[EARLYDEPTH_TEST1] = 00000000, MASK = 000000FF
[EARLYDEPTH_TEST2] = 00000000
[EARLYDEPTH_FUNC] = 00000001, MASK = 000000FF
[EARLYDEPTH_DATA] = 00000000, MASK = 00FFFFFF
[TEXUNIT_CONFIG] = 00011000, MASK = FF00FFFF
[TEXUNIT_CONFIG] = 00010000, MASK = 00FF0000
[TEXUNIT0_SHADOW] = 00000001
[TEXENV_UPDATE_BUFFER] = 00000000, MASK = 00FFFFFF
[TEXENV_BUFFER_COLOR] = FFFFFFFF
[FOG_COLOR] = 00000000
[TEXENV0_SOURCE] = 00000000
-> [TEXENV0_OPERAND] = 00000000
-> [TEXENV0_COMBINER] = 00000000
-> [TEXENV0_COLOR] = FFFFFFFF
-> [TEXENV0_SCALE] = 00000000
[TEXENV1_SOURCE] = 000F000F
-> [TEXENV1_OPERAND] = 00000000
-> [TEXENV1_COMBINER] = 00000000
-> [TEXENV1_COLOR] = FFFFFFFF
-> [TEXENV1_SCALE] = 00000000
[TEXENV2_SOURCE] = 000F000F
-> [TEXENV2_OPERAND] = 00000000
-> [TEXENV2_COMBINER] = 00000000
-> [TEXENV2_COLOR] = FFFFFFFF
-> [TEXENV2_SCALE] = 00000000
[TEXENV2_SOURCE] = 000F000F
-> [TEXENV2_OPERAND] = 00000000
-> [TEXENV2_COMBINER] = 00000000
-> [TEXENV2_COLOR] = FFFFFFFF
-> [TEXENV2_SCALE] = 00000000
[TEXENV4_SOURCE] = 000F000F
-> [TEXENV4_OPERAND] = 00000000
-> [TEXENV4_COMBINER] = 00000000
-> [TEXENV4_COLOR] = FFFFFFFF
-> [TEXENV4_SCALE] = 00000000
[TEXENV5_SOURCE] = 000F000F
-> [TEXENV5_OPERAND] = 00000000
-> [TEXENV5_COMBINER] = 00000000
-> [TEXENV5_COLOR] = FFFFFFFF
-> [TEXENV5_SCALE] = 00000000
[VSH_FLOATUNIFORM_INDEX] = 0000005F
-> [VSH_FLOATUNIFORM_DATA0] = 3B9999BF
-> [VSH_FLOATUNIFORM_DATA1] = 00003F00
-> [VSH_FLOATUNIFORM_DATA2] = 00000000
[VSH_FLOATUNIFORM_INDEX] = 0000005E
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA1] = 00000000
-> [VSH_FLOATUNIFORM_DATA2] = 003D3333
[VSH_FLOATUNIFORM_INDEX] = 80000000
[VSH_FLOATUNIFORM_DATA0] = BF800000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 3C088888
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 3F800000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = BBA3D70A
-> [VSH_FLOATUNIFORM_DATA0] = BF800000
-> [VSH_FLOATUNIFORM_DATA0] = 3F800000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 3F800000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
-> [VSH_FLOATUNIFORM_DATA0] = 00000000
[VSH_BOOLUNIFORM] = 7FFF0000
[PRIMITIVE_CONFIG] = 00000000, MASK = 0000FF00
[RESTART_PRIMITIVE] = 00000001
[INDEXBUFFER_CONFIG] = 80000000
[GEOSTAGE_CONFIG2] = 00000001, MASK = 000000FF
[START_DRAW_FUNC0] = 00000000, MASK = 000000FF
[FIXEDATTRIB_INDEX] = 0000000F
[FIXEDATTRIB_DATA0] = 0000003E
-> [FIXEDATTRIB_DATA1] = 00004690
-> [FIXEDATTRIB_DATA2] = 00469000
[FIXEDATTRIB_DATA0] = 3F000000
-> [FIXEDATTRIB_DATA1] = 00000000
-> [FIXEDATTRIB_DATA2] = 003F0000
[FIXEDATTRIB_DATA0] = 0000003E
-> [FIXEDATTRIB_DATA1] = 00004440
-> [FIXEDATTRIB_DATA2] = 00459000
[FIXEDATTRIB_DATA0] = 3F000000
-> [FIXEDATTRIB_DATA1] = 00003F00
-> [FIXEDATTRIB_DATA2] = 00000000
[FIXEDATTRIB_DATA0] = 0000003E
-> [FIXEDATTRIB_DATA1] = 00004440
-> [FIXEDATTRIB_DATA2] = 00472C00
[FIXEDATTRIB_DATA0] = 3F00003F
-> [FIXEDATTRIB_DATA1] = 00000000
-> [FIXEDATTRIB_DATA2] = 00000000
[START_DRAW_FUNC0] = 00000001, MASK = 000000FF
[GEOSTAGE_CONFIG2] = 00000000, MASK = 000000FF
[VTX_FUNC] = 00000001
[FRAMEBUFFER_FLUSH] = 00000001
[FRAMEBUFFER_INVALIDATE] = 00000001
[EARLYDEPTH_CLEAR] = 00000001
[FINALIZE] = 12345678
Note:
The GPU is very picky and won't forgive you any mistake. It hangs if you look at it wrong. The cmd list interface also needs 16 bytes aligned buffers (both address and size). If unaligned pad it with a FINALIZE cmd like libctru does. And you need vertex shaders apparently. Without the GPU won't do anything.

As for the wrongly defined reg names: It was late at evening and the autor of the parser got a bit lazy.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

Many thanks!!! That drawing code was life saving! That worked without problems, and helped to figure out what was wrong in my drawing code.

My first mistake was not remembering that even simple 2D graphics without depth buffer do still require fully correct X,Y,z,W coordinates (especially W=1.0 for avoiding perspective division).
And the second mistake was having disabled all output mask bits in the OPDESC descriptors (because 3dbrew said that they are disabled when "setting" the bits; but they apparently meant disabled when "setting to zero").

For the init code, I got away with only one init line: [GPUREG_START_DRAW_FUNC0]=000000001h (assuming that the other registers still contain power-up default values).
Drawing a single triangle is working fine with/without using command lists. The manual register writes might be useful for testing some inner workings of the GPU. But yeah, normally, command lists should be better, especially when drawing multiple triangles.
GPU hangs happened to me once or then, like when sending wrong number of vertices. In such cases the hang can be actually useful for finding the bug. But maybe I'll come across more frustating unexplained hangs in next some weeks.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

I have tried the write to "GPUREG_START_DRAW_FUNC0" and indeed it fixes the hang. Meanwhile i have added GPU rendering to my AGB FIRM and scaling with linear interpolation. Looks pretty sick. I hardcoded the GPU cmd lists currently but eventually a port of citro3d should be done.

Before this i also revisited GSP code and fixed screen init once and for all. There were a number of small problems like not waiting for MCU events when MCU reg 0x22 got written. It's all in the previously linked open_agb_firm repo.


Notes for the attached build:
Place rom.gba and optionally rom.sav on the root of the SD card and boot the FIRM. It will output on the topscreen. Save type hardcoded to 32 KiB SRAM.
Attachments
open_agb_firm.zip
(30.53 KiB) Downloaded 274 times
coto
Posts: 102
Joined: Wed Mar 06, 2019 6:00 pm
Location: Chile

Re: 3DS reverse engineering

Post by coto »

profi200 wrote: Fri May 01, 2020 12:20 pm I have tried the write to "GPUREG_START_DRAW_FUNC0" and indeed it fixes the hang. Meanwhile i have added GPU rendering to my AGB FIRM and scaling with linear interpolation. Looks pretty sick. I hardcoded the GPU cmd lists currently but eventually a port of citro3d should be done.

Before this i also revisited GSP code and fixed screen init once and for all. There were a number of small problems like not waiting for MCU events when MCU reg 0x22 got written. It's all in the previously linked open_agb_firm repo.


Notes for the attached build:
Place rom.gba and optionally rom.sav on the root of the SD card and boot the FIRM. It will output on the topscreen. Save type hardcoded to 32 KiB SRAM.
That's awesome, stepping up the game I see.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

Uploaded no$gba v3.01 - http://problemkaputt.de/gba.htm - with lots of new details in the GPU specs.

3DS GPU External Registers - Memory Control/Status Registers
A new chapter, describing the GPU memory/status stuff. The more obvious registers are memory traffic counters. For the other registers it's still rather unclear what they are doing, but at least it's now documented that they do exist, and which bits are readable or writeable, and if they do have some relation to this-or-that other registers.

3DS GPU External Registers - Memfill and Memcopy
This contains lots of updates/fixes for the MEMCOPY registers. The width/height values were swapped, the output size turned out to be input size, and the what-was-called-input-size turned out to be an (optional) input gap. The RGBA data formats for Input and Output are now documented. And it contains a couple of notes on the Ready flag: Polling the flag seems to hang the GPU (unless polling it from another register), and Ready can be optionally triggered anywhere at begin, end, or middle of transfer (see the REMAIN register).

3DS GPU Internal Registers - Finalize Interrupt registers
This chapter is now more clear about the differences between acknowledge and request registers. And the mystery about whether or not the IRQ does terminate command list execution is solved: It's optional (see formerly undocumented AUTOSTOP register).

3DS GPU Internal Registers - Geometry Pipeline registers
The Attribute buffer register descriptions are more streamlined (and hopefully, clearer). I wasn't able to write a meaningful description with the old register names, so I've renamed all attribute buffer related registers to ATTR_BUF_xxx (for example, NUMVERTICES and FORMAT_LOW/HIGH are used only for ATTR_BUF, not for FIXEDATTRIB). Oh, and the old FORMAT_LOW/HIGH type/size description was all nonsense (eg. what was called "24bit" turned out to refer to 3-dimensional (X,Y,Z) vectors).
The DRAWELEMENTS feature turned out to be much more simple than expected: It's merely using an extra index list (for re-using entries and saving some memory), and otherwise it does work same as DRAWARRAYS. That, after spending the last some months on scratching my head... what could those elements do? Are they Water and Earth, or are they Triangles with more than three hyperdimensional edges? And could I ever hope to understand such things?

Undocumented Registers and Bits
The undocumented registers are now also mentioned in the GPU chapters. And a few of them are solved:
GPUREG_RENDERBUFFER_DIM_0 appears to be a don't care dummy register?
GPUREG_RENDERBUFFER_DIM_1 has an undocumented flag for horizontally mirroring the buffer.
GPUREG_undoc_10401160h.bit8 allows "flat shading" (using only one color per triangle, instead of interpolating three colors).
GPUREG_undoc_10401180h.bit8-9 can produce striped/dotted output (drawing only each 2nd or 4th pixel).
GPUREG_COLOR_OPERATION.bit24 does also produce striped output.
GPUREG_STAT_NUM_VERTICES_RECEIVED, GPUREG_STAT_NUM_TRIANGLES_RECEIVED, GPUREG_STAT_NUM_TRIANGLES_DISPLAYED are undocumented performance monitoring counters.

Yet unknown things
There are still plenty of unknown/unclear control flags. And I haven't yet looked into lighting and texturing and more complex shader opcodes. And, for more basic things, I often just don't know what the documentation is talking about.
- EARLYDEPTH and FRAGOP_CLIP: maybe some OpenGL people have a better idea what those registers might do?
- Fragment Lighting: I am unsure what "fragment" means in that context... is it just meaning "light settings per polygon"?
- reserved geometry shader subdivision: I have some idea what "subdivision" means, but I would assume that it's a software feature, nothing that could be done by hardware. The "reserved geometry shader" prefix might also hint on the subdivision being done by software, on one of the shader units?
- Texture Environment: Is that something about merging pixels from different textures (and other color parameters) with each other? If so, the six "environments" might be six "steps", alike 1st step=merge this, 2nd step=merge that, 3rd..6th step=do nothing?

16 May 2020 - no$gba version 3.02
- 3ds/vfp/help: multiply note about FMUL X,X,X.. and FMAC Y,Y,Y.. (thanks kemal)
- 3ds/gpu/help: added triangle drawing examples (thanks to profi200 for help)
- 3ds/gpu/help: created I/O map chapters for gpu internal/external registers
- 3ds/gpu/help: renamed several registers, especially ATTR_BUF related ones
- 3ds/gpu/help: swapped/renamed width and height to match up with actual usage
- 3ds/gpu/help: important details/corrections for rendering pipeline registers
- 3ds/gpu/help: better GPUREG_IRQ_xxx and GPU_MEMCOPY descriptions
- 3ds/gpu/help: info on undocumented flat shading and striped/dotted modes
- 3ds/gpu/help: info on undocumented memory traffic and vertex/polygon counters
- 3ds/gpu/help: added undocumented gpu register stubs (inside of gpu chapters)
- 3ds/gpu/help: started to rewrite and rearrange unclear gpu descriptions
- 3ds/gpu/help: removed meaningless sentences alike this register is used to...
- 3ds/gpu/help: removed nonsense definitions alike unsigned 1bit enable flag
- 3ds/gpu/help: gpu external registers: added memory control/status registers
- a22i/float: .float16/24/32/64/80 supports multiple operands (using commas)
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

profi200 wrote: Fri May 01, 2020 12:20 pmThere were a number of small problems like not waiting for MCU events when MCU reg 0x22 got written.
Is that for the "push" bit in MCU[22h].bit1? The bootrom doesn't seem to wait for "push" events, I am wondering what the "push" is good for (and of course, what "push" means at all). The I2C-LCD registers seem to be writeable even before "push", so "push" apparently doesn't mean to power-on the LCD controllers. Other ideas would be:
- Avoid cosmetic glitches (like garbage pixels shown for 2ms at power-up).
- Allow some internal self-calibration to finish.
- Avoid messing up the program flow of the MCU (or Power Managment device), when sending new MCU commands while "push" is still busy.
profi200 wrote: Fri May 01, 2020 12:20 pmMeanwhile i have added GPU rendering to my AGB FIRM and scaling with linear interpolation. Looks pretty sick.
Pretty sick... does that look better/worse than using the official LGYFB scaling hardware?
profi200 wrote: Fri May 01, 2020 12:20 pmopen_agb_firm.zip
Cool, the FCRAM config sequence is interesting. I had tried to set/clear those bits, too, but that didn't work without knowing the correct order/timings. The interrupt handler for IRQ 59h is also interesting (I think that was the last "unknown" interrupt; in terms of being known to be used, without knowing what for).
I've written up some description for the registers...

The registers at 102010xxh are controlling FCRAM mode/delays (and whatever other things):

Code: Select all

10201000h - FCRAM_MODE (R/W)
  0     FCRAM Mode (burst enable or so)  (0=GBA, 1=3DS/Fast)              (R/W)
  1-4   Unknown... (00h..0Fh, usually 0)                                  (R/W)
  5-31  Unused (0)
Bit0 must be cleared in GBA mode (where FCRAM is used to emulate the GBA
cartridge ROM), when not clearing bit0, only about 1% of GBA ROM reads will
correctly read from "addr", and the other 99% will accidentally read from
"addr+20h" (wrapped within 800h-byte pages).
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT.

10201010h - FCRAM_DELAY (R/W)
  0-1   Unknown...  (0..3, usually 0)                                     (R/W)
  2-15  Unused (0)
  16-18 FCRAM Delay (0..7 = Fast..Slow, usually 2)                        (R/W)
  19-31 Unused (0)
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT.

10201020h - FCRAM_OR_WHATEVER (R/W)
  0-3   Unknown... (00h..0Fh, usually 8)                                  (R/W)
  4-7   Unused (0)
  8-11  Unknown... (00h..0Fh, usually 8)                                  (R/W)
  12-30 Unused (0)
  31    Unknown... (0..1, usually 0)                                      (R/W)
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT (probably so... assuming that it works similar as above
registers, and that changes do have some yet-unknown effect at all).
Applying those settings is done via 10141210h, which has only three bits, but they are quite messy and confusing. The best way I've found to describe/use those bits is:

Code: Select all

10141210h - CFG11_GPU_FCRAM_CNT (R/W)
Used to apply changes to Port 102010xxh FCRAM Registers (which is needed only
when switching to GBA mode; or when using nonstandard FCRAM timings).
  0     Apply FCRAM Config     (0=Prepare, 1=Apply; on 0-to-1 transition) (R/W)
  1     Allow 3DS FCRAM access (0=Disable, 1=Enable)                      (R/W)
  2     Apply Busy flag    (0=Ready, 1=Busy; or bit0-1 aren't set to 01h)   (R)
  3-31  Unused (0)
After toggling bit0 from 0-to-1, one must wait until the apply has finished.
Weirdly, the Busy flag cannot be read when bit1=1, however, below are some
working apply sequences:
  Write 0h, Write 1h, Wait until Bit2=0, Write 3h ;best, for GBA and 3DS mode
  Write 0h, Write 1h, Wait until Bit2=0           ;good, for GBA mode only
  Write 0h, Write 3h, do ugly hardcoded delay     ;ugly, for GBA and 3DS mode
Note: ARM11 hangs when accessing FCRAM if bit0 or bit1 are zero, or if apply is
busy (or if GBA mode is active).
I am wondering why it was called "CFG11_GPU_FCRAM_CNT", does it affect the GPU, too?

And, the ARM7 SLEEP interrupt register...

Code: Select all

10141104h - CFG11_TWLMODE_SLEEP (R/W)
  0     ARM7 Wakeup                   (0=No change, 1=Wakeup; clear bit1-2) (W)
  1-2   ARM7 Sleep state              (0=Normal/awake, 1-2=?, 3=Sleeping)   (R)
  3-14  Unused? (0)
  15    Enable IRQ upon ARM7 Sleep    (0=Disable, 1=Enable IRQ 59h)       (R/W)
Allows to trigger an IRQ when ARM7 enters lower power sleep mode (via GBA SWI
03h or NDS7/DSi7 SWI 07h). If so, one should forward the ARM7 wakeup condition
from CFG11_TWLMODE_HID_IRQ to ARM11 register HID_PAD_IRQ, enter sleep mode on
ARM11 side, and (after ARM11 wakeup) wakeup ARM7 via CFG11_TWLMODE_SLEEP.bit0.
Btw. there are some more totally-unknown-what-for registers at 10204000h, is there any code using those?

Anyways, now it would be nice to support GBA/NDS/DSi titles in 3DS wifiboot...

GBA support is quite simple, the main problem would be the savedata.
In no$gba, I am detecting that on the fly (if it's writing to EEPROM area then it must contain EEPROM), but that's working in emulators only. In gbatek, I have a list of library ID strings (EEPROM_Vnnn, FLASH512_Vnnn, etc), that should work fine on 3DS, as long as the strings are present (except that, in case of EEPROM, it doesn't contain size info).
The GBA footers (if present) could be also useful for savetype detection, but I am much too bad about teamwork & social media to find somebody who could create/dump/share one of those footers.
The GBA cartridge RTC can be probably always enabled (at least if the corresponding ROM area is zerofilled; and even if it isn't: ROM reads should still work fine if the RTC is initially set to write-only mode).
The 3DS seems to temporarily store the GBA savedata in a separate partition... that might be done to avoid data corruption/exploits caused by crashed GBA games, and sequential writes might be (slightly) safer/faster when saving with low battery... but I am not too convinced that it's really needed.
Well, and another small issue, one would somehow need to assign a device:\path\filename for the savedata of to the uploaded file.

NDS/DSi support requires a lot of memory and hardware initializations. I have most of that code in unlaunch, but it might be some work to port the whole stuff to 3DS (or execute it after switching to DSi mode).
And there is a big problem with the missing DSi system files. One "big" small issue is that the 3DS doesn't have a DSi System Settings file... I know that the touchscreen calibration is stored in HWCAL files... but how & where are the other settings stored? Like username, country, language, favorite color, etc.?

PS. I've found it easier to disassemble the .firm file in open_agb_firm.zip, instead of trying to find the relevant source code file(s) on the tablet. I hope that I haven't missed some essential information that way (or if I did miss something important, it would be cool if you could point me on the file/function names to look at).
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

nocash wrote: Mon May 18, 2020 2:37 am Is that for the "push" bit in MCU[22h].bit1? The bootrom doesn't seem to wait for "push" events, I am wondering what the "push" is good for (and of course, what "push" means at all). The I2C-LCD registers seem to be writeable even before "push", so "push" apparently doesn't mean to power-on the LCD controllers. Other ideas would be:
- Avoid cosmetic glitches (like garbage pixels shown for 2ms at power-up).
- Allow some internal self-calibration to finish.
- Avoid messing up the program flow of the MCU (or Power Managment device), when sending new MCU commands while "push" is still busy.
MCU events 24-29 correspond to the same bits in reg 0x22. One event for each bit.
The current theory regarding the "push" bits is they control LCD panel power but not necessarily controller power.

Code: Select all

	setupFramebufs(fmtTop, fmtBot);

	*((vu32*)0x10140140) = 0; // REG_CFG11_GPUPROT

	// Reset
	REG_PDN_GPU_CNT = 0x10000;
	wait(12);
	REG_PDN_GPU_CNT = 0x1007F;
	REG_GX_GPU_CLK = 0x100;
	REG_GX_PSC_VRAM = 0;
	REG_GX_PSC_FILL0_CNT = 0;
	REG_GX_PSC_FILL1_CNT = 0;
	REG_GX_PPF_CNT = 0;

	// LCD framebuffer setup.
	setupDislayController(0);
	setupDislayController(1);
	REG_LCD_PDC0_SWAP = 0; // Select framebuf 0.
	REG_LCD_PDC1_SWAP = 0;
	REG_LCD_PDC0_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E; // Start
	REG_LCD_PDC1_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E;

	// LCD reg setup.
	REG_LCD_ABL0_FILL = 1u<<24; // Force blackscreen
	REG_LCD_ABL1_FILL = 1u<<24; // Force blackscreen
	REG_LCD_PARALLAX_CNT = 0;
	REG_LCD_PARALLAX_PWM = 0xA390A39;
	REG_LCD_RST = 0;
	REG_LCD_UNK00C = 0x10001;

	// Register IRQ handlers.
	IRQ_registerHandler(IRQ_PSC0, 14, 0, true, gfxIrqHandler);
	IRQ_registerHandler(IRQ_PSC1, 14, 0, true, gfxIrqHandler);
	IRQ_registerHandler(IRQ_PDC0, 14, 0, true, gfxIrqHandler);
	//IRQ_registerHandler(IRQ_PDC1, 14, 0, true, gfxIrqHandler);
	IRQ_registerHandler(IRQ_PPF, 14, 0, true, gfxIrqHandler);
	IRQ_registerHandler(IRQ_P3D, 14, 0, true, gfxIrqHandler);

	// Clear entire VRAM.
	GX_memoryFill((u32*)VRAM_BANK0, 1u<<9, VRAM_SIZE / 2, 0,
	              (u32*)VRAM_BANK1, 1u<<9, VRAM_SIZE / 2, 0);

	// Backlight and other stuff.
	REG_LCD_ABL0_LIGHT = 0;
	REG_LCD_ABL0_CNT = 0;
	REG_LCD_ABL0_LIGHT_PWM = 0;
	REG_LCD_ABL1_LIGHT = 0;
	REG_LCD_ABL1_CNT = 0;
	REG_LCD_ABL1_LIGHT_PWM = 0;

	REG_LCD_RST = 1;
	REG_LCD_UNK00C = 0;
	TIMER_sleepMs(10);
	resetLcdsMaybe();
	MCU_controlLCDPower(2u); // Power on LCDs.
	if(MCU_waitEvents(0x3Fu<<24) != 2u<<24) panic();

	// The transfer engine is (sometimes) borked on screen init.
	// Doing a dummy texture copy fixes it.
	// TODO: Proper fix.
	//GX_textureCopy((u32*)RENDERBUF_TOP, 0, (u32*)RENDERBUF_BOT, 0, 16);

	waitLcdsReady();
	REG_LCD_ABL0_LIGHT_PWM = 0x1023E;
	REG_LCD_ABL1_LIGHT_PWM = 0x1023E;
	MCU_controlLCDPower(0x28u); // Power on backlights.
	if(MCU_waitEvents(0x3Fu<<24) != 0x28u<<24) panic();
	g_gfxState.lcdPower = 0x15; // All on.

	// Make sure the fills finished.
	GFX_waitForEvent(GFX_EVENT_PSC0, false);
	GFX_waitForEvent(GFX_EVENT_PSC1, false);
	REG_LCD_ABL0_FILL = 0;
	REG_LCD_ABL1_FILL = 0;

	// GPU stuff.
	REG_GX_GPU_CLK = 0x70100;
	*((vu32*)0x10400050) = 0x22221200;
	*((vu32*)0x10400054) = 0xFF2;

	REG_GX_P3D(GPUREG_IRQ_ACK) = 0;
	REG_GX_P3D(GPUREG_IRQ_CMP) = 0x12345678;
	REG_GX_P3D(GPUREG_IRQ_MASK) = 0xFFFFFFF0;
	REG_GX_P3D(GPUREG_IRQ_AUTOSTOP) = 1;

	// This reg needs to be set to 1 (configuration)
	// before running the first cmd list.
	REG_GX_P3D(GPUREG_START_DRAW_FUNC0) = 1;
nocash wrote: Mon May 18, 2020 2:37 am Pretty sick... does that look better/worse than using the official LGYFB scaling hardware?[
It looks better i would say. A bit blurry but better. Rendering time is ~0.522 ms right now leaving room for more complex scaling and filters. mGBA's pixelated scaling on 3DS is very good and i wanna add it eventually.
nocash wrote: Mon May 18, 2020 2:37 am Cool, the FCRAM config sequence is interesting. I had tried to set/clear those bits, too, but that didn't work without knowing the correct order/timings. The interrupt handler for IRQ 59h is also interesting (I think that was the last "unknown" interrupt; in terms of being known to be used, without knowing what for).
I've written up some description for the registers...

The registers at 102010xxh are controlling FCRAM mode/delays (and whatever other things):

Code: Select all

10201000h - FCRAM_MODE (R/W)
  0     FCRAM Mode (burst enable or so)  (0=GBA, 1=3DS/Fast)              (R/W)
  1-4   Unknown... (00h..0Fh, usually 0)                                  (R/W)
  5-31  Unused (0)
Bit0 must be cleared in GBA mode (where FCRAM is used to emulate the GBA
cartridge ROM), when not clearing bit0, only about 1% of GBA ROM reads will
correctly read from "addr", and the other 99% will accidentally read from
"addr+20h" (wrapped within 800h-byte pages).
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT.

10201010h - FCRAM_DELAY (R/W)
  0-1   Unknown...  (0..3, usually 0)                                     (R/W)
  2-15  Unused (0)
  16-18 FCRAM Delay (0..7 = Fast..Slow, usually 2)                        (R/W)
  19-31 Unused (0)
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT.

10201020h - FCRAM_OR_WHATEVER (R/W)
  0-3   Unknown... (00h..0Fh, usually 8)                                  (R/W)
  4-7   Unused (0)
  8-11  Unknown... (00h..0Fh, usually 8)                                  (R/W)
  12-30 Unused (0)
  31    Unknown... (0..1, usually 0)                                      (R/W)
Changes to this register have no effect until applying them via
CFG11_GPU_FCRAM_CNT (probably so... assuming that it works similar as above
registers, and that changes do have some yet-unknown effect at all).
Applying those settings is done via 10141210h, which has only three bits, but they are quite messy and confusing. The best way I've found to describe/use those bits is:

Code: Select all

10141210h - CFG11_GPU_FCRAM_CNT (R/W)
Used to apply changes to Port 102010xxh FCRAM Registers (which is needed only
when switching to GBA mode; or when using nonstandard FCRAM timings).
  0     Apply FCRAM Config     (0=Prepare, 1=Apply; on 0-to-1 transition) (R/W)
  1     Allow 3DS FCRAM access (0=Disable, 1=Enable)                      (R/W)
  2     Apply Busy flag    (0=Ready, 1=Busy; or bit0-1 aren't set to 01h)   (R)
  3-31  Unused (0)
After toggling bit0 from 0-to-1, one must wait until the apply has finished.
Weirdly, the Busy flag cannot be read when bit1=1, however, below are some
working apply sequences:
  Write 0h, Write 1h, Wait until Bit2=0, Write 3h ;best, for GBA and 3DS mode
  Write 0h, Write 1h, Wait until Bit2=0           ;good, for GBA mode only
  Write 0h, Write 3h, do ugly hardcoded delay     ;ugly, for GBA and 3DS mode
Note: ARM11 hangs when accessing FCRAM if bit0 or bit1 are zero, or if apply is
busy (or if GBA mode is active).
I am wondering why it was called "CFG11_GPU_FCRAM_CNT", does it affect the GPU, too?

And, the ARM7 SLEEP interrupt register...

Code: Select all

10141104h - CFG11_TWLMODE_SLEEP (R/W)
  0     ARM7 Wakeup                   (0=No change, 1=Wakeup; clear bit1-2) (W)
  1-2   ARM7 Sleep state              (0=Normal/awake, 1-2=?, 3=Sleeping)   (R)
  3-14  Unused? (0)
  15    Enable IRQ upon ARM7 Sleep    (0=Disable, 1=Enable IRQ 59h)       (R/W)
Allows to trigger an IRQ when ARM7 enters lower power sleep mode (via GBA SWI
03h or NDS7/DSi7 SWI 07h). If so, one should forward the ARM7 wakeup condition
from CFG11_TWLMODE_HID_IRQ to ARM11 register HID_PAD_IRQ, enter sleep mode on
ARM11 side, and (after ARM11 wakeup) wakeup ARM7 via CFG11_TWLMODE_SLEEP.bit0.
Btw. there are some more totally-unknown-what-for registers at 10204000h, is there any code using those?
Bit 0 of the FCRAM PDN reg seems to be reset, bit 1 some kind of enable/disable bit (FCRAM clock?) and bit 2 acknowledge for bit 1.

The LGY_SLEEP reg i documented a while ago on 3dbrew. Note that bit 0 and 1 must be written at the same time or it will wakeup too early sometimes. Not idea why.
nocash wrote: Mon May 18, 2020 2:37 am Anyways, now it would be nice to support GBA/NDS/DSi titles in 3DS wifiboot...

GBA support is quite simple, the main problem would be the savedata.
In no$gba, I am detecting that on the fly (if it's writing to EEPROM area then it must contain EEPROM), but that's working in emulators only. In gbatek, I have a list of library ID strings (EEPROM_Vnnn, FLASH512_Vnnn, etc), that should work fine on 3DS, as long as the strings are present (except that, in case of EEPROM, it doesn't contain size info).
The GBA footers (if present) could be also useful for savetype detection, but I am much too bad about teamwork & social media to find somebody who could create/dump/share one of those footers.
The GBA cartridge RTC can be probably always enabled (at least if the corresponding ROM area is zerofilled; and even if it isn't: ROM reads should still work fine if the RTC is initially set to write-only mode).
The 3DS seems to temporarily store the GBA savedata in a separate partition... that might be done to avoid data corruption/exploits caused by crashed GBA games, and sequential writes might be (slightly) safer/faster when saving with low battery... but I am not too convinced that it's really needed.
Well, and another small issue, one would somehow need to assign a device:\path\filename for the savedata of to the uploaded file.

NDS/DSi support requires a lot of memory and hardware initializations. I have most of that code in unlaunch, but it might be some work to port the whole stuff to 3DS (or execute it after switching to DSi mode).
And there is a big problem with the missing DSi system files. One "big" small issue is that the 3DS doesn't have a DSi System Settings file... I know that the touchscreen calibration is stored in HWCAL files... but how & where are the other settings stored? Like username, country, language, favorite color, etc.?

PS. I've found it easier to disassemble the .firm file in open_agb_firm.zip, instead of trying to find the relevant source code file(s) on the tablet. I hope that I haven't missed some essential information that way (or if I did miss something important, it would be cool if you could point me on the file/function names to look at).
For save detection i had a hashtable database in mind and talking with endrift (mGBA dev) she agrees on that. But such a database must be built somehow first. Until then i have no choice as to resort to unreliable/inaccurate string search.

I have no idea why it saves on eMMC. I can directly save to the SD card just fine.

For DSi mode the embedded launcher binary (in legacy Process9) needs to be reverse engineered. I have not touched it at all yet. And touchscreen calibration is stored both in 3DS mode accessible locations and NVRAM. It also has been recently found out, that the write protected data at the start of NVRAM is some calibration/whatever stuff. It also contains the WiFi modules MAC address. There have been cases where this data got mysteriously corrupted causing DSi mode bricks (always whitescreen) andrecovering that data is not possible without hardware modifications.

I will never understand this ._. Anyway. Interesting files are probably the ones in here:
https://github.com/profi200/open_agb_fi ... 1/hardware lgy.c, lgyfb.c, lgyfb.dma330.
And in lgyfb.c yes, Twl-/AgbBg uses the same trick of "flickering" V-total to keep framerate in sync. The output from LgyFb is slightly slower than the LCD rrfreshrate and we can't match it exactly unfortunately. But this trick works well.
Attachments
stuff.zip
(100.44 KiB) Downloaded 277 times
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

profi200 wrote: Tue May 19, 2020 6:25 am MCU events 24-29 correspond to the same bits in reg 0x22. One event for each bit.
Good to know! I had missed IRQ 26,27,28,29 in my MCU disassembly. Now I've found them... and IRQ 18,19,20,21 are also used:

Code: Select all

MCU[10h-13h] - MCU Interrupt Flags, bit0-31 (1=IRQ, cleared after read) (R)
MCU[18h-1Bh] - MCU Interrupt Mask, bit0-31 (0=Enable, 1=Disable) (R/W)
  0     Power button press (for 27 "ticks") aka 0.2s
  1     Power button held (for 375 "ticks" aka 3s, turns off after another 12s)
  2     HOME button press (for 5 "ticks") aka 0.04s
  3     HOME button release
  4     Wifi switch button    ;uh, WHAT "switch button" ???
  5     Shell close
  6     Shell open
  7     Fatal hardware condition? (sent when MCU gets reset by Watchdog timer)
  8     Charger removed
  9     Charger plugged in
  10    RTC alarm (when some conditions are met it's
         sent when the current day and month and year
         matches the current RTC time)
         (uh, WHICH conditions? and, does it also
         support hour:minute alarm? and supposedly also
         PLAIN hour:minute WITHOUT day/month/year?)
  11    Accelerometer I2C manual read/write done
  12    Accelerometer new XYZ sample update
  13    Battery dropped below 11%, 6% or 1% (warns at those 3 points)
  14    Battery charging stop (independent of charger state)
  15    Battery charging start
 Nonmaskable(?) interrupts, uh how/why are below "nonmaskable" ???
  16    TSC[10h].bit0=1=Whatever, or BPTWL[11h]=01h=Reset
  17    TSC[10h].bit6=1=Whateverelse
  18    TSC[10h].bit2 changed to zero
  19    TSC[10h].bit2 changed to one
  20    TSC[10h].bit3 changed to zero
  21    TSC[10h].bit3 changed to one
  22    Volume slider position change
  23    Read from BPTWL[00h] version register has occurred
  24    Video Display "push" off
  25    Video Display "push" on
  26    Video Bottom screen backlight off
  27    Video Bottom screen backlight on
  28    Video Top screen backlight off
  29    Video Top screen backlight on
  30    set by mcu sysmodule  ;\uh, flag bits aren't set by MCU (and can't be
  31    set by mcu sysmodule  ;/set by ARM), but maybe ARM sets mask bits..?
TSC[10h] is one of the internal I2C bus registers, I don't know what it's doing, and if it's related to any of the TSC-SPI bus registers (it might be also related to sound/microphone flags in the NDS Powerman SPI registers; which are located inside of the TSC chip on DSi/3DS).
profi200 wrote: Tue May 19, 2020 6:25 am Bit 0 of the FCRAM PDN reg seems to be reset, bit 1 some kind of enable/disable bit (FCRAM clock?) and bit 2 acknowledge for bit 1.
Bit0 and bit1 is more or less same as my findings. I think bit2 is read-only, not a write-able acknowledge bit? And bit2 it seems to be ready flag for 0-to-1 transitions of bit0 (but weirdly, the ready flag gets "invisible" if bit1 is set) (no matter if bit2 is visible, the apply does still finish successfully after some delay, even when toggling bit0 by writing 02h-then-03h instead of 00h-then-01h).
profi200 wrote: Tue May 19, 2020 6:25 am The LGY_SLEEP reg i documented a while ago on 3dbrew. Note that bit 0 and 1 must be written at the same time or it will wakeup too early sometimes.
What do you mean by writing bit 0 and 1? Your code does write (set) only bit0.
profi200 wrote: Tue May 19, 2020 6:25 am It also has been recently found out, that the write protected data at the start of NVRAM is some calibration/whatever stuff.
That was known as "Firmware" header in NDS days (see "DS Firmware Header" and "DS Firmware Wifi Calibration Data" in gbatek).
profi200 wrote: Tue May 19, 2020 6:25 am There have been cases where this data got mysteriously corrupted causing DSi mode bricks (always whitescreen) and recovering that data is not possible without hardware modifications.
Despite of the write-protection pin? Outch. Well, if the 3DS mode is still working, then it might be easy to fix... depending on whether the write-protect pin can be accessed on top or bottom side of the mainboard (or in wifi daughterboard)... or it might easier if there is a way to intentionally bypass the write-protect in software.

I didn't knew about recent changes on the 3dbrew CONFIG11 registers page...

TuxSH has renamed the bugged SHAREDWRAM registers, that's good, except the description for bit1 still looks wrong (bit1 should be unused always 0, on New3DS at least).

And TuxSH has - thankfully - renamed the GPU_STAT and PTM registers to PDN and PDN_WAKE, that sounds more much plausible (and, yes, I've spent sleepless days and nights on trying figure out what PTM could mean... my best guesses were Play TiMe and Program Trace Macrocell).
Anyways, with the PDN_WAKE name in mind, it's quite clear that they are some sort of wake-up flags... I've solved about half of them...

Code: Select all

10141000h - CFG11_PDN_CNT (R or W?)
Unknown, reads as zero?

10141008h - CFG11_PDN_WAKE_ENABLE - CFG11_PTM_0 (R/W)
1014100Ch - CFG11_PDN_WAKE_REASON - CFG11_PTM_1 (R/ack)
  0     Unused (0)                   -
  1     HID_PAD_IRQ                  IRQ 5Bh Controller Buttons
  2     Unused (0)                   -
  3     GPIO_DATA0_DATA_IN.bit2=0    IRQ 60h Hinge is Open
  4     GPIO_DATA1_IRQ_ENABLE.bit0   IRQ 64h Headphone connect
  5     Unused (0)                   -
  6     GPIO_DATA1_IRQ_ENABLE.bit1   IRQ 66h ?
  7     Unknown...?                  IRQ ?   ?
  8     Unknown...?                  IRQ ?   ?
  9-15  Unused (0)                   -
  16    Unknown...?                  IRQ ?   ?
  17    GPIO_DATA3_IRQ_ENABLE.bit0   IRQ 68h C-stick
  18    GPIO_DATA3_IRQ_ENABLE.bit1   IRQ 69h IrDA IRQ
  19    GPIO_DATA3_IRQ_ENABLE.bit2   IRQ 6Ah Gyro IRQ
  20    GPIO_DATA3_IRQ_ENABLE.bit3   IRQ 6Bh ?
  21    GPIO_DATA3_IRQ_ENABLE.bit4   IRQ 6Ch ?
  22    GPIO_DATA3_IRQ_ENABLE.bit5   IRQ 6Dh ?
  23    GPIO_DATA3_IRQ_ENABLE.bit6   IRQ 6Eh ?
  24    GPIO_DATA3_IRQ_ENABLE.bit7   IRQ 6Fh ?
  25    GPIO_DATA3_IRQ_ENABLE.bit8   IRQ 70h TSC[67h:2Bh] (Headphone connect)
  26    GPIO_DATA3_IRQ_ENABLE.bit9   IRQ 71h MCU[10h-1Fh]
  27    GPIO_DATA3_IRQ_ENABLE.bit10  IRQ 72h NFC?
  28    GPIO_DATA3_IRQ_ENABLE.bit11  IRQ 73h ?
  29    Unknown...?                  IRQ ?   ? ... triggers always/often?
  30    GPIO_DATA0_DATA_IN.bit1=0    IRQ 63h Touchscreen Pen is down
  31    GPIO_DATA0_DATA_IN.bit2=1    IRQ 62h Hinge is Closed
Note: TSC triggers on headphone connect, ack by reading TSC[67h:2Bh].
The REASON bits are getting set only if enabled in CFG11_PDN_WAKE_ENABLE (and
the corresponding bits in GPIO_DATA1_IRQ_ENABLE or GPIO_DATA3_IRQ_ENABLE or
HID_PAD_IRQ must be also enabled).
The REASON bits can be cleared by writing 1 (unless the request condition is
still enabled and true).
Caution: Nonzero REASON bits can cause attempts to switch to New3DS mode (via
CFG11_MPCORE_CLKCNT) to hang?
Caution: Above is for New3DS (Old3DS doesn't have NFC and C-stick, and the
other bits are probably all completely different and still unknown).
I don't know how to enter sleep mode though. Probably by writing to PDN_WAKE (and maybe PDN_CNT, too?) and then executing WFI opcode?

TWLMODE renamed to LGY doesn't look bad, I would have kept the CFG11 prefix for CONFIG11 registers though.
Renaming the ARM7 registers to LGY wouldn't be bad either. Most them are GBA specific, so LGY_GBA might be yet better (for the GBA cartridge savedata and RTC stuff).

On the CONFIG11 page, the LGY_MODE description says "waiting for bit 3". It isn't too clear which register that bit is in (and if it is what I think, then it would be bit2, not bit3).

LGY_SLEEP bit1 and bit2 seem to be usually/always same (and both seem to be readonly, cleared when writing bit0=1).
At least, I haven't spotted cases where bit1 and bit2 contain different values.

And CFG11_FCRAM_CNT description... I think the main purpose is to apply changes, not to disable FCRAM.
When entering GBA/NDS mode, the FCRAM is kept enabled on GBA/NDS side, and automatically disabled on 3DS side (regardless of CFG11_FCRAM_CNT).

For NDS mode, it doesn't seem to be required to change Port 10201000h.bit0, I don't know if changing that bit is having any positive/negative effects in NDS mode though.
Last edited by nocash on Wed May 20, 2020 11:54 am, edited 2 times in total.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: 3DS reverse engineering

Post by nocash »

Just looked at the GBA footer hexdump. The entries with file offsets and sizes and padding bytes are actually as guessed... so the format should look as so:

Code: Select all

GBA ROM-images are reportedly stored in .code files (in NCCH .app files, which
can also contain the usual NCCH icon/banner/logo).
Nintendo is using 360h-byte footers in GBA ROM-images (at the end of the .code
file?).
 Config Data:
  000h 4    Unknown (usually 0) (maybe ROM-image Offset, or version)
  004h 4    ROM-image Size (usually Romsize)
  008h 4    Cartridge Type (Port 10018100h, ARM7_SAVE_TYPE)
  00Ch 4    Unknown (usually 0000FFFFh) (guess: savedata fillvalue?)
  010h 4    Unknown (Port 10018120h, reportedly 1561662 or 2607238) ;\maybe
  014h 4    Unknown (Port 10018124h, reportedly 156166  or 577077 ) ; write
  018h 4    Unknown (Port 10018128h, reportedly 134     or 388    ) ; erase
  01Ch 4    Unknown (Port 1001812Ch, reportedly 187667  or 201072 ) ;/timings?
  020h 4    LCD Ghosting (01h..FFh) (uh, what is that?)
  024h 300h LCD Video LUT (guess: maybe for Port 10400484h/10400584h or so?)
  324h 0Ch  Padding (0)
 1st Descriptor:
  330h 4    Descriptor Type  (00h=ROM-Image)
  334h 4    ROM-image Offset (usually 0)
  338h 4    ROM-image Size   (usually Romsize)
  33Ch 4    Padding (0)
 2nd Descriptor:
  340h 4    Descriptor Type  (01h=Config Data)
  344h 4    Config Offset    (usually Romsize+0)
  348h 4    Config Size      (usually 324h)
  34Ch 4    Padding (0)
 Footer Entrypoint (in last 10h-byte of .code file):
  350h 4    GBA Footer ID          (".CAA")
  354h 4    Maybe Version          (must be 1)
  358h 4    Descriptor List Offset (usually Romsize+330h)
  35Ch 4    Descriptor List Size   (usually 20h) (2*10h)
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: 3DS reverse engineering

Post by Pokun »

nocash wrote: Wed May 20, 2020 10:46 am

Code: Select all

MCU[18h-1Bh] - MCU Interrupt Mask, bit0-31 (0=Enable, 1=Disable) (R/W)
...
  4     Wifi switch button    ;uh, WHAT "switch button" ???
On the right edge of the old 3DS, but it's supposedly missing on the New 3DS (wifi can only be turned off in the home menu).
It's the same type of momentary slide switch as the DS Lite's power switch, and it's even in the exact same location. It slides back when you release the finger like a spring.

It's detected by the MCU using a maskable interrupt? So it IS a software thing after all.
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

nocash wrote: Wed May 20, 2020 10:46 am Bit0 and bit1 is more or less same as my findings. I think bit2 is read-only, not a write-able acknowledge bit? And bit2 it seems to be ready flag for 0-to-1 transitions of bit0 (but weirdly, the ready flag gets "invisible" if bit1 is set) (no matter if bit2 is visible, the apply does still finish successfully after some delay, even when toggling bit0 by writing 02h-then-03h instead of 00h-then-01h).
I think you misunderstood. Bit 2 isn't a bit for you to acknowledge on CPU side but the hardware acknowledges when bit 1 is set. A ready flag if you will. I think you are supposed to wait for the ready flag to become clear if bit 1 is clear and wait for it to become set if bit 1 is set. The former is definitely true because that's what AgbBg does.
nocash wrote: Wed May 20, 2020 10:46 am What do you mean by writing bit 0 and 1? Your code does write (set) only bit0.
Well, think about this line again "REG_LGY_SLEEP |= 1u; // Aknowledge and wakeup.". Notice how it's a read-modify-write :wink:
nocash wrote: Wed May 20, 2020 10:46 am Despite of the write-protection pin? Outch. Well, if the 3DS mode is still working, then it might be easy to fix... depending on whether the write-protect pin can be accessed on top or bottom side of the mainboard (or in wifi daughterboard)... or it might easier if there is a way to intentionally bypass the write-protect in software.
Yes, i have no idea how this is possible. Flash memory should hold the data for at least 20 years and the hardware write protection should prevent any software caused corruption.
nocash wrote: Wed May 20, 2020 10:46 am I don't know how to enter sleep mode though. Probably by writing to PDN_WAKE (and maybe PDN_CNT, too?) and then executing WFI opcode?
According to code reverse engineered from K11 TuxSH showed me recently it works like this:
Disable all non-essential IRQs,
Check if bit 15 in CFG11_PDN_CNT is set and if not disable all VRAM banks and the GPU clock (CFG11_GPU_CNT bit 16) + waiting for bit 15 in CFG11_PDN_CNT to become unset.
Disable FCRAM clock(?) by unsetting bit 1 in CFG11_FCRAM_CNT and waiting for the ack bit to get cleared.
Then write 1 to CFG11_PDN_CNT and execute a wfi.
nocash wrote: Wed May 20, 2020 10:46 am On the CONFIG11 page, the LGY_MODE description says "waiting for bit 3". It isn't too clear which register that bit is in (and if it is what I think, then it would be bit2, not bit3).
Bit 3 was a mistake. It's bit 2.
nocash wrote: Wed May 20, 2020 10:46 am LGY_SLEEP bit1 and bit2 seem to be usually/always same (and both seem to be readonly, cleared when writing bit0=1).
At least, I haven't spotted cases where bit1 and bit2 contain different values.
No idea what bit 2 is good for.
nocash wrote: Wed May 20, 2020 10:46 am And CFG11_FCRAM_CNT description... I think the main purpose is to apply changes, not to disable FCRAM.
When entering GBA/NDS mode, the FCRAM is kept enabled on GBA/NDS side, and automatically disabled on 3DS side (regardless of CFG11_FCRAM_CNT).

For NDS mode, it doesn't seem to be required to change Port 10201000h.bit0, I don't know if changing that bit is having any positive/negative effects in NDS mode though.
To apply changes you have to disable clock temporarily and/or reset.

I dunno if this is required for DSi mode. In AgbBg there is a debug printf() you literally can't miss. Something along the lines of "[agb] FinishFcramForAgb". I have not looked into TwlBg to confirm if this is needed.
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

Just to let you know i made a big discovery. There are 3 identical TMIO SD(IO) controllers on 3DS. 2 of them are always located at the same register space (Controller 1 at 0x10006000 and controller 2 at 0x10122000). What's new is that you can switch controller 3 between ARM9 (0x10007000) or ARM11 (0x10100000, default). And now comes the surprise. You can connect the SD card port (0) to either controller 1 or 3 so you can make the SD card ARM11 accessible. Details are here:

https://www.3dbrew.org/wiki/CONFIG9_Reg ... 9_SDMMCCTL
assemblyx69
Posts: 17
Joined: Wed Oct 02, 2019 12:50 am

Re: 3DS reverse engineering

Post by assemblyx69 »

-
Last edited by assemblyx69 on Tue Nov 03, 2020 9:34 pm, edited 1 time in total.
profi200
Posts: 66
Joined: Fri May 10, 2019 4:48 am

Re: 3DS reverse engineering

Post by profi200 »

Most of these footers are unofficial. They should not be used as a reference. Only official ones.

And by the way what we called ghosting is probably alpha blending. That ghosting effect looks like they are blending multiple frames over each other.
Post Reply