You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/Palettes.md
+68-54Lines changed: 68 additions & 54 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,72 +22,82 @@ Value | Color
22
22
2 | Dark gray
23
23
3 | Black
24
24
25
-
In CGB Mode the Color Palettes are taken from [CGB Palette Memory](<#LCD Color Palettes (CGB only)>)
25
+
In CGB Mode the color palettes are taken from [CGB palette memory](<#LCD Color Palettes (CGB only)>)
26
26
instead.
27
27
28
-
### FF48 - OBP0 (Object Palette 0 Data) (R/W) - Non CGB Mode Only
28
+
### FF48 - OBP0 (OBJ Palette 0 Data) (R/W), FF49 - OBP1 (OBJ Palette 1 Data) (R/W) - Both Non CGB Mode Only
29
29
30
-
This register assigns gray shades to the color indexes of the OBJs that use this palette. It works exactly
31
-
like BGP (FF47), except that the lower two bits are ignored because
32
-
sprite index 00 means transparent.
33
-
34
-
### FF49 - OBP1 (Object Palette 1 Data) (R/W) - Non CGB Mode Only
35
-
36
-
This register assigns gray shades to the color indexes of the OBJs that use this palette. It works exactly
37
-
like BGP (FF47), except that the lower two bits are ignored because
38
-
sprite index 00 means transparent.
30
+
These registers assigns gray shades to the color indexes of the OBJs that use the corresponding palette.
31
+
They work exactly like BGP, except that the lower two bits are ignored because color index 0 is transparent for OBJs.
39
32
40
33
## LCD Color Palettes (CGB only)
41
34
35
+
The CGB has a small amount of RAM used to store its color palettes. Unlike most
36
+
of the hardware interface, palette RAM (or *CRAM* for *Color RAM*) is not
37
+
accessed directly, but instead through the following registers:
38
+
42
39
### FF68 - BCPS/BGPI (Background Color Palette Specification or Background Palette Index) - CGB Mode Only
43
40
44
-
This register is used to address a byte in the CGBs Background Palette
45
-
Memory. Each two byte in that memory define a color value. The first 8
46
-
bytes define Color 0-3 of Palette 0 (BGP0), and so on for BGP1-7.
41
+
This register is used to address a byte in the CGB's background palette RAM.
42
+
Since there are 8 palettes, 8 palettes × 4 colors/palette × 2 bytes/color = 64 bytes
43
+
can be addressed.
47
44
48
45
```
49
46
Bit 7 Auto Increment (0=Disabled, 1=Increment after Writing)
50
-
Bit 5-0 Index (00-3F)
47
+
Bit 5-0 Address ($00-3F)
51
48
```
52
49
53
-
Data can be read/written to/from the specified index address through
54
-
Register FF69. When the Auto Increment bit is set then the index is
55
-
automatically incremented after each **write** to FF69. Auto Increment has
56
-
no effect when **reading** from FF69, so the index must be manually
57
-
incremented in that case. Writing to FF69 during rendering still causes
58
-
auto-increment to occur.
50
+
First comes BGP0 color number 0, then BGP0 color number 1, BGP0 color number 2, BGP0 color number 3,
51
+
BGP1 color number 0, and so on. Thus, address $03 allows accessing the second (upper)
52
+
byte of BGP0 color #1 via BCPD, which contains the color's blue and upper green bits.
59
53
60
-
Unlike the following, this register can be accessed outside VBlank and
61
-
HBlank.
54
+
Data can be read from or written to the specified CRAM address through
55
+
BCPD/BGPD. If the Auto Increment bit is set, the index gets
56
+
incremented after each **write** to BCPD. Auto Increment has
57
+
no effect when **reading** from BCPD, so the index must be manually
58
+
incremented in that case. Writing to BCPD during rendering still causes
59
+
auto-increment to occur, despite the write being blocked.
60
+
61
+
Unlike BCPD, this register can be accessed outside VBlank and HBlank.
62
62
63
63
### FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only
64
64
65
-
This register allows to read/write data to the CGBs Background Palette
66
-
Memory, addressed through Register FF68. Each color is defined by two
67
-
bytes (Bit 0-7 in first byte).
65
+
This register allows to read/write data to the CGBs background palette memory,
66
+
addressed through BCPS/BGPI. Each color is stored as little-endian RGB555:
68
67
69
68
```
70
-
Bit 0-4 Red Intensity (00-1F)
71
-
Bit 5-9 Green Intensity (00-1F)
72
-
Bit 10-14 Blue Intensity (00-1F)
69
+
Bit 0-4 Red Intensity ($00-1F)
70
+
Bit 5-9 Green Intensity ($00-1F)
71
+
Bit 10-14 Blue Intensity ($00-1F)
73
72
```
74
73
75
-
Much like VRAM, data in Palette Memory cannot be read/written during the
76
-
time when the LCD Controller is reading from it. (That is when the STAT
77
-
register indicates Mode 3). Note: All background colors are initialized
78
-
as white by the boot ROM, but it's a good idea to initialize at least
79
-
one color yourself (for example if you include a soft-reset mechanic).
74
+
Much like VRAM, data in palette memory cannot be read or written during the time
75
+
when the PPU is reading from it, that is, [Mode 3](<#LCD Status Register>).
76
+
77
+
::: tip NOTE
78
+
79
+
All background colors are initialized as white by the boot ROM, however it is a
80
+
good idea to initialize all colors yourself, e.g. if implementing
81
+
a soft-reset mechanic.
80
82
81
-
### 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
83
+
:::
82
84
83
-
These registers are used to initialize the Sprite Palettes OBP0-7,
84
-
identically as described above for Background Palettes. Note that four
85
-
colors may be defined for each OBP Palettes - but only Color 1-3 of each
86
-
Sprite Palette can be displayed, Color 0 is always transparent, and can
87
-
be initialized to a don't care value or plain never initialized.
85
+
### FF6A - OCPS/OBPI (OBJ Color Palette Specification / OBJ Palette Index), FF6B - OCPD/OBPD (OBJ Color Palette Data / OBJ Palette Data) - Both CGB Mode Only
88
86
89
-
Note: All sprite colors are left uninitialized by the boot ROM, and are
90
-
somewhat random.
87
+
These registers function exactly like BCPS and BCPD respectively; the 64 bytes
88
+
of OBJ palette memory are entirely separate from Background palette memory, but
89
+
function the same.
90
+
91
+
Note that while 4 colors are stored per OBJ palette, color #0 is never used, as
92
+
it's always transparent. It's thus fine to write garbage values, or even leave
93
+
color #0 uninitialized.
94
+
95
+
::: tip NOTE
96
+
97
+
The boot ROM leaves all object colors uninitialized (and thus somewhat random),
98
+
aside from setting the first byte of OBJ0 color #0 to $00, which is unused.
99
+
100
+
:::
91
101
92
102
### RGB Translation by CGBs
93
103
@@ -96,14 +106,14 @@ somewhat random.
96
106
When developing graphics on PCs, note that the RGB values will have
97
107
different appearance on CGB displays as on VGA/HDMI monitors calibrated
98
108
to sRGB color. Because the GBC is not lit, the highest intensity will
99
-
produce Light Gray color rather than White. The intensities are not
100
-
linear; the values 10h-1Fh will all appear very bright, while medium and
101
-
darker colors are ranged at 00h-0Fh.
109
+
produce light gray rather than white. The intensities are not
110
+
linear; the values $10-$1F will all appear very bright, while medium and
111
+
darker colors are ranged at $00-0F.
102
112
103
113
The CGB display's pigments aren't perfectly saturated. This means the
104
-
colors mix quite oddly; increasing intensity of only one R,G,B color
105
-
will also influence the other two R,G,B colors. For example, a color
106
-
setting of 03EFh (Blue=0, Green=1Fh, Red=0Fh) will appear as Neon Green
114
+
colors mix quite oddly: increasing the intensity of only one R/G/B color
115
+
will also influence the other two R/G/B colors. For example, a color
116
+
setting of $03EF (Blue=$00, Green=$1F, Red=$0F) will appear as Neon Green
107
117
on VGA displays, but on the CGB it'll produce a decently washed out
108
118
Yellow. See the image above.
109
119
@@ -113,21 +123,25 @@ Even though GBA is described to be compatible to CGB games, most CGB
113
123
games are completely unplayable on older GBAs because most colors are
114
124
invisible (black). Of course, colors such like Black and White will
115
125
appear the same on both CGB and GBA, but medium intensities are arranged
116
-
completely different. Intensities in range 00h..07h are invisible/black
126
+
completely different. Intensities in range $00–07 are invisible/black
117
127
(unless eventually under best sunlight circumstances, and when gazing at
118
128
the screen under obscure viewing angles), unfortunately, these
119
129
intensities are regularly used by most existing CGB games for medium and
120
130
darker colors.
121
131
132
+
::: tip WORKAROUND
133
+
122
134
Newer CGB games may avoid this effect by changing palette data when
123
-
detecting GBA hardware ([see
124
-
how](<#Detecting CGB (and GBA) functions>)).
125
-
Based on measurement of GBC and GBA palettes using the [144p Test
126
-
Suite](https://github.com/pinobatch/240p-test-mini/tree/master/gameboy) ROM, a fairly close approximation is GBA = GBC \* 3/4 + 8h for
127
-
each R,G,B intensity. The result isn't quite perfect, and it may turn
135
+
detecting GBA hardware ([see how](<#Detecting CGB (and GBA) functions>)).
136
+
Based on measurements of GBC and GBA palettes using the
137
+
[144p Test Suite](https://github.com/pinobatch/240p-test-mini/tree/master/gameboy),
138
+
a fairly close approximation is `GBA = GBC × 3/4 + $08` for each R/G/B
139
+
component. The result isn't quite perfect, and it may turn
128
140
out that the color mixing is different also; anyways, it'd be still
129
141
ways better than no conversion.
130
142
143
+
:::
144
+
131
145
This problem with low brightness levels does not affect later GBA SP
132
146
units and Game Boy Player. Thus ideally, the player should have control
@@ -390,8 +390,8 @@ The table above was obtained from Mooneye-GB tests [`acceptance/boot_hwio-dmg0`]
390
390
[`RP`]: <#FF56 - RP - CGB Mode Only - Infrared Communications Port>
391
391
[`BCPS`]: <#FF68 - BCPS/BGPI (Background Color Palette Specification or Background Palette Index) - CGB Mode Only>
392
392
[`BCPD`]: <#FF69 - BCPD/BGPD (Background Color Palette Data or Background Palette Data) - CGB Mode Only>
393
-
[`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>
394
-
[`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>
393
+
[`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>
394
+
[`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>
When the PPU is reading a particular part of video memory,
46
-
that memory is inaccessible to the CPU.
45
+
When the PPU is accessing some video-related memory, that memory is inaccessible
46
+
to the CPU: writes are ignored, and reads return garbage values (usually $FF).
47
47
48
-
- During modes 2 and 3, the CPU cannot access OAM (FE00h-FE9Fh).
49
-
- During mode 3, the CPU cannot access VRAM or CGB Palette Data
50
-
(FF69,FF6B).
48
+
- During modes 2 and 3, the CPU cannot access [OAM](<#VRAM Sprite Attribute Table (OAM)>) ($FE00-FE9F).
49
+
- 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>)
50
+
($FF69,$FF6B).
51
51
52
-
Mode | Action | Duration | Accessible video memory
52
+
Mode | Action | Duration | Accessible video memory
Unlike most game consoles, the Game Boy can pause the dot clock briefly,
62
-
adding dots to mode 3's duration. It routinely takes a 6 to 11 dot
63
-
break to fetch sprite patterns between background tile pattern fetches.
62
+
making Mode 3 longer and Mode 0 shorter. It routinely takes a 6 to 11 dot
63
+
break to fetch an OBJ's tile between background tile pattern fetches.
64
64
On DMG and GBC in DMG mode, mid-scanline writes to [`BGP`](<#FF47 - BGP (BG Palette Data) (R/W) - Non CGB Mode Only>)
65
-
allow observing this behavior, as a sprite delay shifts the effect of a
66
-
write to the left by that many dots.
65
+
allow observing this behavior, as the delay from drawing an OBJ shifts the
66
+
write's effect to the left by that many dots.
67
67
68
68
Three things are known to pause the dot clock:
69
69
70
-
- 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.
70
+
- 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.
71
71
- Window: An active window pauses for at least 6 dots, as the background fetching mechanism starts over at the left side of the window.
72
-
- 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.
72
+
- 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.
0 commit comments