diff --git a/src/Palettes.md b/src/Palettes.md index 93f9b4cd..8578c25f 100644 --- a/src/Palettes.md +++ b/src/Palettes.md @@ -22,72 +22,82 @@ Value | Color 2 | Dark gray 3 | Black -In CGB Mode the Color Palettes are taken from [CGB Palette Memory](<#LCD Color Palettes (CGB only)>) +In CGB Mode the color palettes are taken from [CGB palette memory](<#LCD Color Palettes (CGB only)>) instead. -### FF48 - OBP0 (Object Palette 0 Data) (R/W) - Non CGB Mode Only +### FF48 - OBP0 (OBJ Palette 0 Data) (R/W), FF49 - OBP1 (OBJ Palette 1 Data) (R/W) - Both Non CGB Mode Only -This register assigns gray shades to the color indexes of the OBJs that use this palette. It works exactly -like BGP (FF47), except that the lower two bits are ignored because -sprite index 00 means transparent. - -### FF49 - OBP1 (Object Palette 1 Data) (R/W) - Non CGB Mode Only - -This register assigns gray shades to the color indexes of the OBJs that use this palette. It works exactly -like BGP (FF47), except that the lower two bits are ignored because -sprite index 00 means transparent. +These registers assigns gray shades to the color indexes of the OBJs that use the corresponding palette. +They work exactly like BGP, except that the lower two bits are ignored because color index 0 is transparent for OBJs. ## LCD Color Palettes (CGB only) +The CGB has a small amount of RAM used to store its color palettes. Unlike most +of the hardware interface, palette RAM (or *CRAM* for *Color RAM*) is not +accessed directly, but instead through the following registers: + ### FF68 - BCPS/BGPI (Background Color Palette Specification or Background Palette Index) - CGB Mode Only -This register is used to address a byte in the CGBs Background Palette -Memory. Each two byte in that memory define a color value. The first 8 -bytes define Color 0-3 of Palette 0 (BGP0), and so on for BGP1-7. +This register is used to address a byte in the CGB's background palette RAM. +Since there are 8 palettes, 8 palettes × 4 colors/palette × 2 bytes/color = 64 bytes +can be addressed. ``` Bit 7 Auto Increment (0=Disabled, 1=Increment after Writing) -Bit 5-0 Index (00-3F) +Bit 5-0 Address ($00-3F) ``` -Data can be read/written to/from the specified index address through -Register FF69. When the Auto Increment bit is set then the index is -automatically incremented after each **write** to FF69. Auto Increment has -no effect when **reading** from FF69, so the index must be manually -incremented in that case. Writing to FF69 during rendering still causes -auto-increment to occur. +First comes BGP0 color number 0, then BGP0 color number 1, BGP0 color number 2, BGP0 color number 3, +BGP1 color number 0, and so on. Thus, address $03 allows accessing the second (upper) +byte of BGP0 color #1 via BCPD, which contains the color's blue and upper green bits. -Unlike the following, this register can be accessed outside VBlank and -HBlank. +Data can be read from or written to the specified CRAM address through +BCPD/BGPD. If the Auto Increment bit is set, the index gets +incremented after each **write** to BCPD. Auto Increment has +no effect when **reading** from BCPD, so the index must be manually +incremented in that case. Writing to BCPD during rendering still causes +auto-increment to occur, despite the write being blocked. + +Unlike BCPD, this register can be accessed outside VBlank and HBlank. ### FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only -This register allows to read/write data to the CGBs Background Palette -Memory, addressed through Register FF68. Each color is defined by two -bytes (Bit 0-7 in first byte). +This register allows to read/write data to the CGBs background palette memory, +addressed through BCPS/BGPI. Each color is stored as little-endian RGB555: ``` -Bit 0-4 Red Intensity (00-1F) -Bit 5-9 Green Intensity (00-1F) -Bit 10-14 Blue Intensity (00-1F) +Bit 0-4 Red Intensity ($00-1F) +Bit 5-9 Green Intensity ($00-1F) +Bit 10-14 Blue Intensity ($00-1F) ``` -Much like VRAM, data in Palette Memory cannot be read/written during the -time when the LCD Controller is reading from it. (That is when the STAT -register indicates Mode 3). Note: All background colors are initialized -as white by the boot ROM, but it's a good idea to initialize at least -one color yourself (for example if you include a soft-reset mechanic). +Much like VRAM, data in palette memory cannot be read or written during the time +when the PPU is reading from it, that is, [Mode 3](<#LCD Status Register>). + +::: tip NOTE + +All background colors are initialized as white by the boot ROM, however it is a +good idea to initialize all colors yourself, e.g. if implementing +a soft-reset mechanic. -### FF6A - OCPS/OBPI (Object Color Palette Specification or Sprite Palette Index), FF6B - OCPD/OBPD (Object Color Palette Data or Sprite Palette Data) - Both CGB Mode Only +::: -These registers are used to initialize the Sprite Palettes OBP0-7, -identically as described above for Background Palettes. Note that four -colors may be defined for each OBP Palettes - but only Color 1-3 of each -Sprite Palette can be displayed, Color 0 is always transparent, and can -be initialized to a don't care value or plain never initialized. +### FF6A - OCPS/OBPI (OBJ Color Palette Specification / OBJ Palette Index), FF6B - OCPD/OBPD (OBJ Color Palette Data / OBJ Palette Data) - Both CGB Mode Only -Note: All sprite colors are left uninitialized by the boot ROM, and are -somewhat random. +These registers function exactly like BCPS and BCPD respectively; the 64 bytes +of OBJ palette memory are entirely separate from Background palette memory, but +function the same. + +Note that while 4 colors are stored per OBJ palette, color #0 is never used, as +it's always transparent. It's thus fine to write garbage values, or even leave +color #0 uninitialized. + +::: tip NOTE + +The boot ROM leaves all object colors uninitialized (and thus somewhat random), +aside from setting the first byte of OBJ0 color #0 to $00, which is unused. + +::: ### RGB Translation by CGBs @@ -96,14 +106,14 @@ somewhat random. When developing graphics on PCs, note that the RGB values will have different appearance on CGB displays as on VGA/HDMI monitors calibrated to sRGB color. Because the GBC is not lit, the highest intensity will -produce Light Gray color rather than White. The intensities are not -linear; the values 10h-1Fh will all appear very bright, while medium and -darker colors are ranged at 00h-0Fh. +produce light gray rather than white. The intensities are not +linear; the values $10-$1F will all appear very bright, while medium and +darker colors are ranged at $00-0F. The CGB display's pigments aren't perfectly saturated. This means the -colors mix quite oddly; increasing intensity of only one R,G,B color -will also influence the other two R,G,B colors. For example, a color -setting of 03EFh (Blue=0, Green=1Fh, Red=0Fh) will appear as Neon Green +colors mix quite oddly: increasing the intensity of only one R/G/B color +will also influence the other two R/G/B colors. For example, a color +setting of $03EF (Blue=$00, Green=$1F, Red=$0F) will appear as Neon Green on VGA displays, but on the CGB it'll produce a decently washed out Yellow. See the image above. @@ -113,21 +123,25 @@ Even though GBA is described to be compatible to CGB games, most CGB games are completely unplayable on older GBAs because most colors are invisible (black). Of course, colors such like Black and White will appear the same on both CGB and GBA, but medium intensities are arranged -completely different. Intensities in range 00h..07h are invisible/black +completely different. Intensities in range $00–07 are invisible/black (unless eventually under best sunlight circumstances, and when gazing at the screen under obscure viewing angles), unfortunately, these intensities are regularly used by most existing CGB games for medium and darker colors. +::: tip WORKAROUND + Newer CGB games may avoid this effect by changing palette data when -detecting GBA hardware ([see -how](<#Detecting CGB (and GBA) functions>)). -Based on measurement of GBC and GBA palettes using the [144p Test -Suite](https://github.com/pinobatch/240p-test-mini/tree/master/gameboy) ROM, a fairly close approximation is GBA = GBC \* 3/4 + 8h for -each R,G,B intensity. The result isn't quite perfect, and it may turn +detecting GBA hardware ([see how](<#Detecting CGB (and GBA) functions>)). +Based on measurements of GBC and GBA palettes using the +[144p Test Suite](https://github.com/pinobatch/240p-test-mini/tree/master/gameboy), +a fairly close approximation is `GBA = GBC × 3/4 + $08` for each R/G/B +component. The result isn't quite perfect, and it may turn out that the color mixing is different also; anyways, it'd be still ways better than no conversion. +::: + This problem with low brightness levels does not affect later GBA SP units and Game Boy Player. Thus ideally, the player should have control of this brightness correction. diff --git a/src/Power_Up_Sequence.md b/src/Power_Up_Sequence.md index dc701e61..5808b9a8 100644 --- a/src/Power_Up_Sequence.md +++ b/src/Power_Up_Sequence.md @@ -376,8 +376,8 @@ The table above was obtained from Mooneye-GB tests [`acceptance/boot_hwio-dmg0`] [`LYC`]: <#FF45 - LYC (LY Compare) (R/W)> [`DMA`]: <#FF46 - DMA (DMA Transfer and Start Address) (R/W)> [`BGP`]: <#FF47 - BGP (BG Palette Data) (R/W) - Non CGB Mode Only> -[`OBP0`]: <#FF48 - OBP0 (Object Palette 0 Data) (R/W) - Non CGB Mode Only> -[`OBP1`]: <#FF49 - OBP1 (Object Palette 1 Data) (R/W) - Non CGB Mode Only> +[`OBP0`]: <#FF48 - OBP0 (OBJ Palette 0 Data) (R/W), FF49 - OBP1 (OBJ Palette 1 Data) (R/W) - Both Non CGB Mode Only> +[`OBP1`]: <#FF48 - OBP0 (OBJ Palette 0 Data) (R/W), FF49 - OBP1 (OBJ Palette 1 Data) (R/W) - Both Non CGB Mode Only> [`WY`]: <#FF4A - WY (Window Y Position) (R/W), FF4B - WX (Window X Position + 7) (R/W)> [`WX`]: <#FF4A - WY (Window Y Position) (R/W), FF4B - WX (Window X Position + 7) (R/W)> [`KEY1`]: <#FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch> @@ -390,8 +390,8 @@ The table above was obtained from Mooneye-GB tests [`acceptance/boot_hwio-dmg0`] [`RP`]: <#FF56 - RP - CGB Mode Only - Infrared Communications Port> [`BCPS`]: <#FF68 - BCPS/BGPI (Background Color Palette Specification or Background Palette Index) - CGB Mode Only> [`BCPD`]: <#FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only> -[`OCPS`]: <#FF6A - OCPS/OBPI (Object Color Palette Specification or Sprite Palette Index), FF6B - OCPD/OBPD (Object Color Palette Data or Sprite Palette Data) - Both CGB Mode Only> -[`OCPD`]: <#FF6A - OCPS/OBPI (Object Color Palette Specification or Sprite Palette Index), FF6B - OCPD/OBPD (Object Color Palette Data or Sprite Palette Data) - Both CGB Mode Only> +[`OCPS`]: <#FF6A - OCPS/OBPI (OBJ Color Palette Specification / OBJ Palette Index), FF6B - OCPD/OBPD (OBJ Color Palette Data / OBJ Palette Data) - Both CGB Mode Only> +[`OCPD`]: <#FF6A - OCPS/OBPI (OBJ Color Palette Specification / OBJ Palette Index), FF6B - OCPD/OBPD (OBJ Color Palette Data / OBJ Palette Data) - Both CGB Mode Only> [`OPRI`]: <#FF6C - OPRI - CGB Mode Only - Object Priority Mode> [`SVBK`]: <#FF70 - SVBK - CGB Mode Only - WRAM Bank> [FF72]: <#FF72 - Bits 0-7 (Read/Write), FF73 - Bits 0-7 (Read/Write)> diff --git a/src/STAT.md b/src/STAT.md index 14d3b54f..0d6775f1 100644 --- a/src/STAT.md +++ b/src/STAT.md @@ -15,8 +15,8 @@ Bit 4 - Mode 1 VBlank STAT Interrupt source (1=Enable) (Read/Write) Bit 3 - Mode 0 HBlank STAT Interrupt source (1=Enable) (Read/Write) Bit 2 - LYC=LY Flag (0=Different, 1=Equal) (Read Only) Bit 1-0 - Mode Flag (Mode 0-3, see below) (Read Only) - 0: In HBlank - 1: In VBlank + 0: HBlank + 1: VBlank 2: Searching OAM 3: Transferring Data to LCD Controller ``` @@ -29,7 +29,7 @@ It is constantly updated. Bits 3-6 select which sources are used for [the STAT interrupt](<#INT 48 - STAT Interrupt>). The LCD controller operates on a 2^22 Hz = 4.194 MHz dot clock. An -entire frame is 154 scanlines, 70224 dots, or 16.74 ms. On scanlines 0 +entire frame is 154 scanlines = 70224 dots = 16.74 ms. On scanlines 0 through 143, the PPU cycles through modes 2, 3, and 0 once every 456 dots. Scanlines 144 through 153 are mode 1. @@ -42,34 +42,34 @@ Mode 0 ___000___000___000___000___000___000________________000 Mode 1 ____________________________________11111111111111_____ ``` -When the PPU is reading a particular part of video memory, -that memory is inaccessible to the CPU. +When the PPU is accessing some video-related memory, that memory is inaccessible +to the CPU: writes are ignored, and reads return garbage values (usually $FF). -- During modes 2 and 3, the CPU cannot access OAM (FE00h-FE9Fh). -- During mode 3, the CPU cannot access VRAM or CGB Palette Data - (FF69,FF6B). +- During modes 2 and 3, the CPU cannot access [OAM](<#VRAM Sprite Attribute Table (OAM)>) ($FE00-FE9F). +- During mode 3, the CPU cannot access VRAM or [CGB palette data registers](<#FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only>) + ($FF69,$FF6B). -Mode | Action | Duration | Accessible video memory +Mode | Action | Duration | Accessible video memory -----|------------------------------------------------------------------|--------------------------------------------------------------------|------------------------- - 2 | Searching OAM for OBJs whose (X,Y) coordinates overlap this line | 80 dots (19 µs) | VRAM, CGB palettes - 3 | Reading OAM and VRAM to generate the picture | 168 to 291 dots (40 to 60 µs) depending on sprite count | None - 0 | Horizontal blanking | 85 to 208 dots (20 to 49 µs) depending on previous mode 3 duration | VRAM, OAM, CGB palettes - 1 | Vertical blanking | 4560 dots (1087 µs, 10 scanlines) | VRAM, OAM, CGB palettes + 2 | Searching OAM for OBJs whose Y coordinate overlap this line | 80 dots (19 µs) | VRAM, CGB palettes + 3 | Reading OAM and VRAM to generate the picture | 168 to 291 dots (40 to 60 µs) depending on sprite count | None + 0 | Nothing (HBlank) | 85 to 208 dots (20 to 49 µs) depending on previous mode 3 duration | VRAM, OAM, CGB palettes + 1 | Nothing (VBlank) | 4560 dots (1087 µs, 10 scanlines) | VRAM, OAM, CGB palettes ## Properties of STAT modes Unlike most game consoles, the Game Boy can pause the dot clock briefly, -adding dots to mode 3's duration. It routinely takes a 6 to 11 dot -break to fetch sprite patterns between background tile pattern fetches. +making Mode 3 longer and Mode 0 shorter. It routinely takes a 6 to 11 dot +break to fetch an OBJ's tile between background tile pattern fetches. On DMG and GBC in DMG mode, mid-scanline writes to [`BGP`](<#FF47 - BGP (BG Palette Data) (R/W) - Non CGB Mode Only>) -allow observing this behavior, as a sprite delay shifts the effect of a -write to the left by that many dots. +allow observing this behavior, as the delay from drawing an OBJ shifts the +write's effect to the left by that many dots. Three things are known to pause the dot clock: -- Background scrolling: If `SCX mod 8` is not zero at the start of the scanline, rendering is paused for that many dots while the shifter discards that many pixels from the leftmost tile. +- Background scrolling: If `SCX % 8` is not zero at the start of the scanline, rendering is paused for that many dots while the shifter discards that many pixels from the leftmost tile. - Window: An active window pauses for at least 6 dots, as the background fetching mechanism starts over at the left side of the window. -- Sprites: Each sprite usually pauses for `11 - min(5, (x + SCX) mod 8)` dots. Because sprite fetch waits for background fetch to finish, a sprite's cost depends on its position relative to the left side of the background tile under it. It's greater if a sprite is directly aligned over the background tile, less if the sprite is to the right. If the sprite's left side is over the window, use `255 - WX` for `SCX` in this formula. +- Sprites: Each sprite usually pauses for `11 - min(5, (x + SCX) % 8)` dots. Because sprite fetch waits for background fetch to finish, a sprite's cost depends on its position relative to the left side of the background tile under it. It's greater if a sprite is directly aligned over the background tile, less if the sprite is to the right. If the sprite's left side is over the window, use `255 - WX` instead of `SCX` in this formula. ::: warning TO BE VERIFIED diff --git a/src/Scrolling.md b/src/Scrolling.md index c13e1ce2..48f2a9e1 100644 --- a/src/Scrolling.md +++ b/src/Scrolling.md @@ -1,19 +1,19 @@ # LCD Position and Scrolling -These registers can be accessed even during Mode 3, but they have no -effect until the end of the current scanline. +These registers can be accessed even during Mode 3, but modifications may not take +effect immediately (see further below). ## FF42 - SCY (Scroll Y) (R/W), FF43 - SCX (Scroll X) (R/W) -Those specify the top-left coordinates of the visible 160x144 pixel area within the -256x256 pixels BG map. Values in the range 0-255 may be used. +Those specify the top-left coordinates of the visible 160×144 pixel area within the +256×256 pixels BG map. Values in the range 0–255 may be used. ## FF44 - LY (LCD Y Coordinate) (R) LY indicates the current horizontal line, which might be about to be drawn, -being drawn, or just been drawn. LY can hold any value from 0 to 153. -The values from 144 to 153 indicate the VBlank period. +being drawn, or just been drawn. LY can hold any value from 0 to 153, with +values from 144 to 153 indicating the VBlank period. ## FF45 - LYC (LY Compare) (R/W) @@ -32,22 +32,35 @@ The Window is visible (if enabled) when both coordinates are in the ranges WX=0..166, WY=0..143 respectively. Values WX=7, WY=0 place the Window at the top left of the screen, completely covering the background. -WX values 0-6 and 166 are unreliable due to hardware bugs. If WX is set -to 0, the window will "stutter" horizontally when SCX changes. -(Depending on SCX modulo 8, behavior is a little complicated so you -should try it yourself.) +::: warning -::: tip MID-FRAME QUIRKS +WX values 0 and 166 are unreliable due to hardware bugs. -While the Windows should work as just mentioned, writing to WX, WY etc. mid-frame shows a more articulated behavior. +If WX is set to 0, the window will "stutter" horizontally when SCX changes +(depending on SCX % 8). -For the window to be displayed on a scanline: +If WX is set to 166, the window will span the entirety of the following +scanline. -- __WY condition was triggered__: i.e. at some point in this frame the value of WY was equal to LY (checked at the start of Mode 2 only) -- __WX condition was triggered__: i.e. the current X coordinate being rendered + 7 was equal to WX +::: + +## Mid-frame behavior + +### Scrolling + +The scroll registers are re-read on each [tile fetch](<#Get Tile>), except for the low 3 bits of SCX, which are only read at the beginning of the scanline (for the initial shifting of pixels). + +All models before the CGB-D read the Y coordinate once for each bitplane (so a very precisely timed SCY write allows "desyncing" them), but CGB-D and later use the same Y coordinate for both no matter what. + +### Window + +While the Window should work as just mentioned, writing to WX, WY etc. mid-frame shows a more articulated behavior. + +For the window to be displayed on a scanline, the following conditions must be met: + +- **WY condition was triggered**: i.e. at some point in this frame the value of WY was equal to LY (checked at the start of Mode 2 only) +- **WX condition was triggered**: i.e. the current X coordinate being rendered + 7 was equal to WX - Window enable bit in LCDC is set -If the WY condition has already been triggered and at the the start of a row the window enable bit was set +If the WY condition has already been triggered and at the the start of a row the window enable bit was set, then resetting that bit before the WX condition gets triggered on that row yields a nice window glitch pixel where the window would have been activated. - -::: diff --git a/src/Tile_Data.md b/src/Tile_Data.md index 100ec07d..3caed2fe 100644 --- a/src/Tile_Data.md +++ b/src/Tile_Data.md @@ -94,12 +94,8 @@ numbers are translated into real colors (or gray shades) depending on the current palettes, except that when the tile is used in a OBJ the color ID 0 means transparent. The palettes are defined through registers [BGP](<#FF47 - BGP (BG Palette Data) (R/W) - Non CGB Mode Only>), -[OBP0](<#FF48 - OBP0 (Object Palette 0 Data) (R/W) - Non CGB Mode Only>) -and -[OBP1](<#FF49 - OBP1 (Object Palette 1 Data) (R/W) - Non CGB Mode Only>) -(Non CGB Mode), and +[OBP0 and OBP1](<#FF48 - OBP0 (OBJ Palette 0 Data) (R/W), FF49 - OBP1 (OBJ Palette 1 Data) (R/W) - Both Non CGB Mode Only>), and [BCPS/BGPI](<#FF68 - BCPS/BGPI (Background Color Palette Specification or Background Palette Index) - CGB Mode Only>), [BCPD/BGPD](<#FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only>), -[OCPS/OBPI and -OCPD/OBPD](<#FF6A - OCPS/OBPI (Object Color Palette Specification or Sprite Palette Index), FF6B - OCPD/OBPD (Object Color Palette Data or Sprite Palette Data) - Both CGB Mode Only>) +[OCPS/OBPI and OCPD/OBPD](<#FF6A - OCPS/OBPI (OBJ Color Palette Specification / OBJ Palette Index), FF6B - OCPD/OBPD (OBJ Color Palette Data / OBJ Palette Data) - Both CGB Mode Only>) (CGB Mode).