Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 68 additions & 54 deletions src/Palettes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand All @@ -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.
8 changes: 4 additions & 4 deletions src/Power_Up_Sequence.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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)>
Expand Down
38 changes: 19 additions & 19 deletions src/STAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -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.

Expand All @@ -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

Expand Down
Loading