Changes

→‎LCD Source Framebuffer Setup: Correct typo in mathematical calculation.
Line 9: Line 9:  
! Name
 
! Name
 
! Comments
 
! Comments
 +
|-
 +
| 0x1EF00000
 +
| 0x10400000
 +
| 4
 +
| Hardware ID
 +
| Bit2: new model
 
|-
 
|-
 
| 0x1EF00004
 
| 0x1EF00004
Line 32: Line 38:  
| 4
 
| 4
 
| VRAM bank control
 
| VRAM bank control
| Bits 8-11 = bank[i] disabled; other bits are unused
+
| Bits 8-11 = bank[i] disabled; other bits are unused.
 
|-
 
|-
 
| 0x1EF00034
 
| 0x1EF00034
Line 38: Line 44:  
| 4
 
| 4
 
| GPU Busy
 
| GPU Busy
| Bit31 = cmd-list busy, bit27 = PSC0 busy, bit26 = PSC1 busy.
+
| Bit26 = PSC0, bit27 = PSC1, Bit30 = PPF, Bit31 = P3D
 
|-
 
|-
 
| 0x1EF00050
 
| 0x1EF00050
Line 140: Line 146:     
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.
 
All of these registers must be accessed with 32bit operations regardless of the registers' actual bit size.
 +
 +
The naming of these parameters reflects the physical characteristics of the displays, and not the way the 3DS is normally held.
 +
 +
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.
 +
 +
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
 +
 +
The horizontal timing parameter order is as follows (values may overflow through HTotal register value):
 +
0x10 < 0x14 <= 0x60.LO <= 0x04 <= 0x60.HI <= 0x08 <= 0x0C <= 0x10
 +
0x18 <= 0x60.LO
 +
 +
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.
 +
There is an inherent latch order, where if two simultenaous events occur, one event wins over another.
 +
 +
Known latched modes (in order):
 +
- HSync (triggers a line to the LCD to move to the next line)
 +
- Back porch (area between HSync and border being displayed, no pixels pushed, min 16 pixel clocks, otherwise the screen gets glitchy)
 +
- Left border start (no image data is being displayed, just a configurable solid color)
 +
- Image start (pixel data is being DMA'd from video memory or main RAM)
 +
- Right border start/Image end (border color is being displayed after the main image)
 +
- Unknown synchronization (supposed to be probably right border end, but this mode seems to be broken or not do anything)
 +
- Front porch (no pixels pushed, 68 clock min, otherwise the screen doesn't sync properly, and really glitches out)
    
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
Line 147: Line 175:  
|-
 
|-
 
| 0x00
 
| 0x00
| H-total (V-total on not physically rotated screens).
+
| HTotal
| 12bits.
+
| The total width of a timing scanline. In other words, this is the horizontal refresh clock divider value.
   −
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).
+
HClock = PClock / (HTotal + 1)
 
|-
 
|-
 
| 0x04
 
| 0x04
| HBlank timer(?)
+
| HStart
| Seems to determine the horizontal blanking interval.
+
| Determines when the image is going to be displayed in the visible region (register 0x60).
 
  −
 
  −
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.
  −
 
  −
Setting this to higher than <code>HTotal - HDisp</code> will make the displayed image misaligned to the right.
  −
 
  −
Setting this to higher than <code>HTotal</code> seems to make the horizontal synchronization never happen.
   
|-
 
|-
 
| 0x08
 
| 0x08
| ?
+
| HBR
| must be >= REG#0x00
+
| Right border start(?). Does nothing.
 +
 
 +
While this register seems to have no impact on the image whatsoever, it still has to be set to a valid value.
 +
|  
 
|-
 
|-
 
| 0x0C
 
| 0x0C
| ?
+
| HPF
| must be >= REG#0x08
+
| Front porch. The image is blanked during this period, and no pixels are pushed to the LCD.
 +
 
 +
Unknown why, but a single dot of red is displayed before entering this mode.
 
|-
 
|-
 
| 0x10
 
| 0x10
| Window X start (LgyFb)
+
| HSync
| Offsets the viewing window on the display's physical X co-ordinate relative to its front porch end.
+
| Triggers a HSync pulse.
   −
Outside of LgyFb changing this value only seems to cause weird pixel interpolation and blurryness.
+
Based on behavior, this needs to last at least a pixel clock for the LCD to register the sync.
 
|-
 
|-
 
| 0x14
 
| 0x14
| Window X end (LgyFb)
+
| HPB
| Window X start + window width - 1 is written here to set the end display scan pixel offset.
+
| Back porch? Has to be at least one bigger than HSync, otherwise HSync never triggers.
   −
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.
+
The display is blank, and the LCD displays nothing in this period (doesn't push pixels).
 
|-
 
|-
 
| 0x18
 
| 0x18
| Window Y start (LgyFb)
+
| HBL
| Offsets the viewing window on the display's physical Y co-ordinate relative to the first visible scanline.
+
| Left border trigger treshold. Enables pushing pixels to the display.
 +
 
 +
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.
 +
 
 +
Can be lower than HSync, as the back porch is what takes the controller out of HSync.
 +
 
 +
Must be <= HDisp start (reg 0x60 low u16), otherwise no pixels will be pushed due to a glitched state.
 
|-
 
|-
| 0x20
+
| 0x1C
| Low: Window Y end (LgyFb)
+
| H Interrupt timing
High: ???
+
| Made up from two u16 values, PDC interrupt line is asserted when HCount == low u16, and most likely deasserted when HCount == high u16.
| Low: Window Y start + window height - 1 is written here to set the last display scanline relative to the first visible scanline.
     −
High: This is cleared to zero when displaying LgyFb. Outside of LgyFb this doesn't really seem to do anything useful.
+
There seems to be some limitations though:
 +
* low u16 must be smaller than high u16
 +
* if low u16 is less than HTotal then high u16 must also be smaller than HTotal
 +
* setting low u16 to >= HTotal disables the interrupt ever firing
   −
 
+
This is configured by gsp in a way so that low u16 equals to HTotal, meaning the HSync interrupt will never fire.
??? 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.
+
|-
 +
| 0x20
 +
| low u16: ???
 +
high u16: ???
 +
| ???
 
|-
 
|-
 
| 0x24
 
| 0x24
| V-total (H-total on not physically rotated screens).
+
| VTotal
| Total scanlines including porches/sync timing. Setting this to 494 for the topscreen lowers framerate to about 50.040660858 Hz.
+
| Total height of the timing window. Can be interpreted as the vertical clock divider.
 +
 
 +
VClock = PClock / (HTotal + 1) / (VTotal + 1)
 +
 
 +
Setting this to 494 lowers framerate to about 50.040660858 Hz ((268111856 / 24) / (450 + 1) / (494 + 1)).
 
|-
 
|-
 
| 0x28
 
| 0x28
| VBlank timer(?)
+
| ?
 
| Seems to determine the vertical blanking interval.
 
| Seems to determine the vertical blanking interval.
   Line 213: Line 255:  
|-
 
|-
 
| 0x30
 
| 0x30
| VTotal
+
| ?
 
| 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.
 
| 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.
 
|-
 
|-
Line 223: Line 265:  
| Vertical data offset(?)
 
| Vertical data offset(?)
 
| ??? 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.
 
| ??? 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.
 +
|-
 +
| 0x40
 +
| V Interrupt timing
 +
| 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.
 +
 +
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.
 
|-
 
|-
 
| 0x44
 
| 0x44
Line 234: Line 282:  
| 0x4C
 
| 0x4C
 
| Overscan filler color
 
| Overscan filler color
|  
+
| 24bits(? top 8bits ignored)
 +
 
 +
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.
 
|-
 
|-
 
| 0x50
 
| 0x50
| Horizontal position counter
+
| HCount
| read-only
+
| Horizontal "beam position" counter. Note that this value does not equal to the current pixel being drawn.
 
|-
 
|-
 
| 0x54
 
| 0x54
| Horizontal scanline (HBlank) counter
+
| VCount
| read-only
+
| Vertical "beam position" counter. Note that the scanline being drawn isn't equal to this value.
 
|-
 
|-
 
| 0x5C
 
| 0x5C
 
| ???
 
| ???
| low u16: framebuffer width
+
| low u16: Image width (including some offset?)
high u16: framebuffer height??? (seems to be unused)
+
high u16: Image height??? (seems to be unused)
 
|-
 
|-
 
| 0x60
 
| 0x60
| ???
+
| HDisp
| low u16: timing data(?)
+
| low u16: Image start (border --> pixel data)
high u16: framebuffer total width (amount of pixels blitted regardless of framebuffer width)
+
high u16: Image end (pixel data --> border)
 
|-
 
|-
 
| 0x64
 
| 0x64
Line 268: Line 318:  
|-
 
|-
 
| 0x70
 
| 0x70
| Framebuffer format
+
| Framebuffer format and other settings
| Bit0-15: framebuffer format, bit16-31: unknown
+
| See [[#Framebuffer_format|framebuffer format]]
 
|-
 
|-
 
| 0x74
 
| 0x74
 
| PDC control
 
| PDC control
 
| Bit 0: Enable display controller.
 
| Bit 0: Enable display controller.
Bit 8: H(Blank?) IRQ mask (0 = enabled).
+
Bit 8: HBlank IRQ mask (0 = enabled).
 
Bit 9: VBlank IRQ mask (0 = enabled).
 
Bit 9: VBlank IRQ mask (0 = enabled).
 
Bit 10: Error IRQ mask? (0 = enabled).
 
Bit 10: Error IRQ mask? (0 = enabled).
Line 284: Line 334:  
Bit 4: Currently displaying framebuffer?
 
Bit 4: Currently displaying framebuffer?
 
Bit 8: Reset FIFO?
 
Bit 8: Reset FIFO?
Bit 16: H(Blank?) IRQ status/ack. Write 1 to aknowledge.
+
Bit 16: HBlank IRQ status/ack. Write 1 to aknowledge.
 
Bit 17: VBlank IRQ status/ack.
 
Bit 17: VBlank IRQ status/ack.
 
Bit 18: Error IRQ status/ack?
 
Bit 18: Error IRQ status/ack?
Line 299: Line 349:  
| 0x90
 
| 0x90
 
| Framebuffer stride
 
| Framebuffer stride
| Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).
+
| 32bits (bottom 3bits ignored?)
 +
 
 +
Distance in bytes between the start of two framebuffer rows (must be a multiple of 8).
 +
 
 +
In other words, this can be interpreted as the amount to add to the framebuffer pointer after displaying a scanline.
 +
 
 +
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.
 +
 
 +
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.
 
|-
 
|-
 
| 0x94
 
| 0x94
 
| Framebuffer B first address
 
| Framebuffer B first address
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.
+
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.
 
|-
 
|-
 
| 0x98
 
| 0x98
 
| Framebuffer B second address
 
| Framebuffer B second address
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen.
+
| For top screen, this is the right eye 3D framebuffer. Unused for bottom screen in userland.
 
|}
 
|}
   Line 317: Line 375:  
|-
 
|-
 
| 2-0
 
| 2-0
| Color format
+
| [[#Framebuffer_color_formats|Color format]]
|-
  −
| 3
  −
| ?
   
|-
 
|-
 
| 5-4
 
| 5-4
| Framebuffer scanline output mode (interlace config)
+
| Framebuffer scanline output mode (framebuffer interleave config)
    
  0 - A  (output image as normal)
 
  0 - A  (output image as normal)
  1 - AA (output a single line twice, aka framebuffer A is interlaced with itself)
+
  1 - AA (output a single line twice, so framebuffer A is interleaved with itself)
  2 - AB (interlace framebuffer A and framebuffer B)
+
  2 - AB (interleave framebuffer A and framebuffer B)
 
  3 - BA (same as above, but the line from framebuffer B is outputted first)
 
  3 - BA (same as above, but the line from framebuffer B is outputted first)
   Line 336: Line 391:  
|-
 
|-
 
| 6
 
| 6
| Scan doubling enable* (used by top screen)
+
| Scan doubling enable?* (used by top screen)
 
|-
 
|-
 
| 7
 
| 7
Line 342: Line 397:  
|-
 
|-
 
| 9-8
 
| 9-8
| Value 1 = unknown: get rid of rainbow strip on top of screen, 3 = unknown: black screen.
+
| DMA size
 +
 
 +
0 -  4 words (32 bytes)
 +
1 -  8 words (64 bytes)
 +
2 - 16 words (128 bytes)
 +
3 - ???
 +
 
 +
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.
 
|-
 
|-
| 15-10
+
| 31-16
| Unused?
+
| Unknown
 
|}
 
|}
    
* 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.
 
* 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.
 +
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.
     
23

edits