From 11b6276b57e4f069df3230b9dfb07206c1e0679f Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Thu, 17 Aug 2023 01:00:26 +0200 Subject: [PATCH 1/8] Rename "rendering" to "graphics" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the section more obvious, and the page itself an overview of the graphics at a high level—the *rendering* process will be described separately in one of the upcoming commits. --- src/{Rendering.md => Graphics.md} | 2 +- src/SUMMARY.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{Rendering.md => Graphics.md} (99%) diff --git a/src/Rendering.md b/src/Graphics.md similarity index 99% rename from src/Rendering.md rename to src/Graphics.md index 91fee3b4..5d7929d1 100644 --- a/src/Rendering.md +++ b/src/Graphics.md @@ -1,4 +1,4 @@ -# Rendering Overview +# Graphics Overview The Game Boy outputs graphics to a 160×144 pixel LCD, using a quite complex mechanism to facilitate rendering. diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ce625ca1..5ffb4e94 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -15,7 +15,7 @@ # I/O Ports - [Summary](./Hardware_Reg_List.md) -- [Rendering](./Rendering.md) +- [Graphics](./Graphics.md) - [Tile Data](./Tile_Data.md) - [Tile Maps](./Tile_Maps.md) - [OAM](./OAM.md) From 2512bdf3d52e1bcfff759534ea4a4c1f53140b31 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Thu, 17 Aug 2023 01:31:22 +0200 Subject: [PATCH 2/8] Create rendering overview doc Move related information there, to avoid burying it in the middle of hardware reg documentation. Will reorganise and clean it up next. --- src/Audio.md | 2 +- src/Rendering.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++ src/STAT.md | 65 +------------------------------------ src/SUMMARY.md | 3 +- src/pixel_fifo.md | 11 ------- 5 files changed, 87 insertions(+), 77 deletions(-) create mode 100644 src/Rendering.md diff --git a/src/Audio.md b/src/Audio.md index effd4259..5d3d9c19 100644 --- a/src/Audio.md +++ b/src/Audio.md @@ -36,7 +36,7 @@ The speaker merges back the two channels, losing the stereo aspect entirely. The Game Boy's sound chip is called the APU. -The APU runs off the same master clock as the rest of the Game Boy, which is to say, it is fully synced with the CPU and [PPU](<#Rendering Overview>). +The APU runs off the same master clock as the rest of the Game Boy, which is to say, it is fully synced with the CPU and [PPU](<#Graphics Overview>). This also means that the APU runs about 2.4% faster on the SGB1, increasing frequencies by as much and thus sounding slightly higher-pitched. The SGB2 rectifies this issue. diff --git a/src/Rendering.md b/src/Rendering.md new file mode 100644 index 00000000..2e655b3d --- /dev/null +++ b/src/Rendering.md @@ -0,0 +1,83 @@ +# Rendering overview + +::: tip TERMINOLOGY + +All references to a "dot" are meant as dots (one 4 MiHz time unit). +Dots remain the same regardless of whether the CPU is in [double speed](<#FF4D — KEY1 (CGB Mode only): Prepare speed switch>). + +::: + +
+ +The following diagram shows how a Game Boy frame is decomposed: + +
+ +{{#include imgs/ppu_modes_timing.svg:2:}} + +
+ +## STAT modes + +The LCD controller operates on a 2^22 Hz = 4.194 MHz dot clock. An +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. + +The following sequence is typical when the display is enabled: + +``` +Mode 2 2_____2_____2_____2_____2_____2___________________2____ +Mode 3 _33____33____33____33____33____33__________________3___ +Mode 0 ___000___000___000___000___000___000________________000 +Mode 1 ____________________________________11111111111111_____ +``` + +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](<#VRAM Sprite Attribute Table (OAM)>) ($FE00-FE9F). +- During mode 3, the CPU cannot access VRAM or [CGB palette data registers](<#LCD Color Palettes (CGB only)>) + ($FF69,$FF6B). + +Mode | Action | Duration | Accessible video memory +-----|------------------------------------------------------------------|--------------------------------------------------------------------|------------------------- + 2 | Searching OAM for OBJs whose Y coordinate overlap this line | 80 dots | VRAM, CGB palettes + 3 | Reading OAM and VRAM to generate the picture | 168 to 291 dots, depending on object count | None + 0 | Nothing (HBlank) | 85 to 208 dots, depending on previous mode 3 duration | VRAM, OAM, CGB palettes + 1 | Nothing (VBlank) | 4560 dots (10 scanlines) | VRAM, OAM, CGB palettes + +## Properties of STAT modes + +Unlike most game consoles, the Game Boy can pause the dot clock briefly, +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 (Non-CGB Mode only): BG palette data>) +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 % 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. +- Objects: Each object usually pauses for `11 - min(5, (x + SCX) % 8)` dots. + Because object fetch waits for background fetch to finish, an object's cost depends on its position relative to the left side of the background tile under it. It's greater if an object is directly aligned over the background tile, less if the object is to the right. If the object's left side is over the window, use `255 - WX` instead of `SCX` in this formula. + +::: warning TO BE VERIFIED + +The exact pause duration for window start is +not confirmed; it may have the same background fetch finish delay as +an object. If two objects' left sides are over the same background or +window tile, the second may pause for fewer dots. + +::: + +A hardware quirk in the monochrome Game Boy makes the LCD interrupt +sometimes trigger when writing to STAT (including writing \$00) during +OAM scan, HBlank, VBlank, or LY=LYC. It behaves as if \$FF were +written for one cycle, and then the written value were written the next +cycle. Because the GBC in DMG mode does not have this quirk, two games +that depend on this quirk (Ocean's *Road Rash* and Vic Tokai's *Xerd +no Densetsu*) will not run on a GBC. + +TODO diff --git a/src/STAT.md b/src/STAT.md index a657073c..515a4fdc 100644 --- a/src/STAT.md +++ b/src/STAT.md @@ -33,72 +33,9 @@ Bit 1-0 - Mode Flag (Mode 0-3, see below) (Read Only) 3: Transferring Data to LCD Controller ``` -The two lower STAT bits show the current status of the PPU. +The two lower STAT bits show the current [status of the PPU](<#STAT modes>). Bit 2 is set when [LY](<#FF44 — LY: LCD Y coordinate \[read-only\]>) contains the same value as [LYC](<#FF45 — LYC: LY compare>). It is constantly updated. Bits 3-6 select which sources are used for [the STAT interrupt](<#INT $48 — STAT interrupt>). - -## STAT modes - -The LCD controller operates on a 2^22 Hz = 4.194 MHz dot clock. An -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. - -The following sequence is typical when the display is enabled: - -``` -Mode 2 2_____2_____2_____2_____2_____2___________________2____ -Mode 3 _33____33____33____33____33____33__________________3___ -Mode 0 ___000___000___000___000___000___000________________000 -Mode 1 ____________________________________11111111111111_____ -``` - -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](<#VRAM Sprite Attribute Table (OAM)>) ($FE00-FE9F). -- During mode 3, the CPU cannot access VRAM or [CGB palette data registers](<#LCD Color Palettes (CGB only)>) - ($FF69,$FF6B). - -Mode | Action | Duration | Accessible video memory ------|------------------------------------------------------------------|--------------------------------------------------------------------|------------------------- - 2 | Searching OAM for OBJs whose Y coordinate overlap this line | 80 dots | VRAM, CGB palettes - 3 | Reading OAM and VRAM to generate the picture | 168 to 291 dots, depending on object count | None - 0 | Nothing (HBlank) | 85 to 208 dots, depending on previous mode 3 duration | VRAM, OAM, CGB palettes - 1 | Nothing (VBlank) | 4560 dots (10 scanlines) | VRAM, OAM, CGB palettes - -## Properties of STAT modes - -Unlike most game consoles, the Game Boy can pause the dot clock briefly, -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 (Non-CGB Mode only): BG palette data>) -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 % 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. -- Objects: Each object usually pauses for `11 - min(5, (x + SCX) % 8)` dots. - Because object fetch waits for background fetch to finish, an object's cost depends on its position relative to the left side of the background tile under it. It's greater if an object is directly aligned over the background tile, less if the object is to the right. If the object's left side is over the window, use `255 - WX` instead of `SCX` in this formula. - -::: warning TO BE VERIFIED - -The exact pause duration for window start is -not confirmed; it may have the same background fetch finish delay as -an object. If two objects' left sides are over the same background or -window tile, the second may pause for fewer dots. - -::: - -A hardware quirk in the monochrome Game Boy makes the LCD interrupt -sometimes trigger when writing to STAT (including writing \$00) during -OAM scan, HBlank, VBlank, or LY=LYC. It behaves as if \$FF were -written for one cycle, and then the written value were written the next -cycle. Because the GBC in DMG mode does not have this quirk, two games -that depend on this quirk (Ocean's *Road Rash* and Vic Tokai's *Xerd -no Densetsu*) will not run on a GBC. diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 5ffb4e94..9a57ad7f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -24,7 +24,8 @@ - [LCD Status Registers](./STAT.md) - [Scrolling](./Scrolling.md) - [Palettes](./Palettes.md) - - [Pixel FIFO](./pixel_fifo.md) + - [Rendering](./Rendering.md) + - [Pixel FIFO](./pixel_fifo.md) - [Audio](./Audio.md) - [Audio Registers](./Audio_Registers.md) - [Audio Details](./Audio_details.md) diff --git a/src/pixel_fifo.md b/src/pixel_fifo.md index 1c77a7d2..fc3d7de6 100644 --- a/src/pixel_fifo.md +++ b/src/pixel_fifo.md @@ -1,16 +1,5 @@ # Pixel FIFO -::: tip TERMINOLOGY - -All references to a dot are meant as dots (4.19 MHz). Dots remain the same regardless of -CGB double speed. -When it is stated that a certain action *lengthens mode 3* it means that mode 0 (HBlank) is -shortened to make up for the additional time in mode 3, as shown in the following diagram. - -::: - -{{#include imgs/ppu_modes_timing.svg:2:}} - ## Introduction FIFO stands for *First In, First Out*. The first pixel to be pushed to the From e7a736dd0e7882208cb786329bc5a832a0a1ba19 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Thu, 17 Aug 2023 02:27:30 +0200 Subject: [PATCH 3/8] Clean up rendering overview (first pass) --- src/Rendering.md | 75 +++++++++++++++++++----------------------------- src/STAT.md | 10 +++++++ 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/Rendering.md b/src/Rendering.md index 2e655b3d..5fa9f3bf 100644 --- a/src/Rendering.md +++ b/src/Rendering.md @@ -17,67 +17,52 @@ The following diagram shows how a Game Boy frame is decomposed: -## STAT modes +TODO: high-level description of the above... and implications ("raster effects") -The LCD controller operates on a 2^22 Hz = 4.194 MHz dot clock. An -entire frame is 154 scanlines = 70224 dots = 16.74 ms. On scanlines 0 +## PPU modes + +The PPU operates on a 222 Hz = 4.194 MHz clock, called the "dot clock". +An 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. -The following sequence is typical when the display is enabled: +While the PPU is accessing some video-related memory, [that memory is inaccessible to the CPU](<#Accessing VRAM and OAM>) (writes are ignored, and reads return garbage values, usually $FF). -``` -Mode 2 2_____2_____2_____2_____2_____2___________________2____ -Mode 3 _33____33____33____33____33____33__________________3___ -Mode 0 ___000___000___000___000___000___000________________000 -Mode 1 ____________________________________11111111111111_____ -``` +Mode | Action | Duration | Accessible video memory +-----|--------------------------------------------|--------------------------------------|------------------------- + 2 | Searching for OBJs which overlap this line | 80 dots | VRAM, CGB palettes + 3 | Sending pixels to the LCD | Between 172 and 289 dots, see below | None + 0 | Waiting until the end of the scanline | 376 - mode 3's duration | VRAM, OAM, CGB palettes + 1 | Waiting until the next frame | 4560 dots (10 scanlines) | VRAM, OAM, CGB palettes -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). +## Mode 3 length -- 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](<#LCD Color Palettes (CGB only)>) - ($FF69,$FF6B). +Unlike most game consoles, the Game Boy does not always output pixels steadily[^crt]: some features cause the rendering process to stall for a couple dots. +Any extra time spent stalling *lengthens* Mode 3; but since scanlines last for a fixed number of dots, Mode 0 is therefore shortened by that same amount of time. -Mode | Action | Duration | Accessible video memory ------|------------------------------------------------------------------|--------------------------------------------------------------------|------------------------- - 2 | Searching OAM for OBJs whose Y coordinate overlap this line | 80 dots | VRAM, CGB palettes - 3 | Reading OAM and VRAM to generate the picture | 168 to 291 dots, depending on object count | None - 0 | Nothing (HBlank) | 85 to 208 dots, depending on previous mode 3 duration | VRAM, OAM, CGB palettes - 1 | Nothing (VBlank) | 4560 dots (10 scanlines) | VRAM, OAM, CGB palettes +Three things can cause Mode 3 "penalties": -## Properties of STAT modes +- **Background scrolling**: At the very beginning of Mode 3, rendering is paused for [`SCX`](<#FF42–FF43 — SCY, SCX: Viewport Y position, X position>) % 8 dots while the same number of pixels are discarded from the leftmost tile. +- **Window**: After the last non-window pixel is emitted, a 6-dot penalty is incurred while the BG fetcher is being set up for the window. +- **Objects**: Each object drawn during the scanline (even partially) incurs a 6- to 11-dot penalty ([see below](<#OBJ penalty algorithm>)). -Unlike most game consoles, the Game Boy can pause the dot clock briefly, -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 (Non-CGB Mode only): BG palette data>) 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 % 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. -- Objects: Each object usually pauses for `11 - min(5, (x + SCX) % 8)` dots. - Because object fetch waits for background fetch to finish, an object's cost depends on its position relative to the left side of the background tile under it. It's greater if an object is directly aligned over the background tile, less if the object is to the right. If the object's left side is over the window, use `255 - WX` instead of `SCX` in this formula. +### OBJ penalty algorithm -::: warning TO BE VERIFIED +Only the OBJ's leftmost pixel matters here, transparent or not; it is designated as "The Pixel" in the following. -The exact pause duration for window start is -not confirmed; it may have the same background fetch finish delay as -an object. If two objects' left sides are over the same background or -window tile, the second may pause for fewer dots. +1. Determine the tile (background or window) that The Pixel is within. (This is affected by horizontal scrolling and/or the window!) +2. If that tile has **not** been considered by a previous OBJ yet: + 1. Count how many of that tile's pixels are to the right of The Pixel. + 2. Subtract 3. + 3. Incur this many dots of penalty, or zero if negative (from waiting for the BG fetch to finish). +3. Incur a flat, 6-dot penalty (from fetching the OBJ's tile). -::: +**Exception**: an OBJ with an OAM X position of 0 (thus, completely off the left side of the screen) always incurs a 11-cycle penalty, regardless of `SCX`. -A hardware quirk in the monochrome Game Boy makes the LCD interrupt -sometimes trigger when writing to STAT (including writing \$00) during -OAM scan, HBlank, VBlank, or LY=LYC. It behaves as if \$FF were -written for one cycle, and then the written value were written the next -cycle. Because the GBC in DMG mode does not have this quirk, two games -that depend on this quirk (Ocean's *Road Rash* and Vic Tokai's *Xerd -no Densetsu*) will not run on a GBC. +TODO: a diagram of some examples would probably help this be much clearer! \>_\< -TODO +[^crt]: The Game Boy can afford to "take pauses", because it writes to a LCD it fully controls; by contrast, home consoles like the NES or SNES are on a schedule imposed by the screen they are hooked up to. Taking pauses arguably simplified the PPU's design while allowing greater flexibility to game developers. diff --git a/src/STAT.md b/src/STAT.md index 515a4fdc..9d5c5865 100644 --- a/src/STAT.md +++ b/src/STAT.md @@ -39,3 +39,13 @@ Bit 2 is set when [LY](<#FF44 — LY: LCD Y coordinate \[read-only\]>) contains It is constantly updated. Bits 3-6 select which sources are used for [the STAT interrupt](<#INT $48 — STAT interrupt>). + +### Spurious STAT interrupts + +A hardware quirk in the monochrome Game Boy makes the LCD interrupt +sometimes trigger when writing to STAT (including writing \$00) during +OAM scan, HBlank, VBlank, or LY=LYC. It behaves as if \$FF were +written for one cycle, and then the written value were written the next +cycle. Because the GBC in DMG mode does not have this quirk, two games +that depend on this quirk (Ocean's *Road Rash* and Vic Tokai's *Xerd +no Densetsu*) will not run on a GBC. From b716279a543f58735b5c5b05ccca85c6f08c6764 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Fri, 18 Aug 2023 00:13:42 +0200 Subject: [PATCH 4/8] Clean up rendering overview (second pass) --- src/Rendering.md | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Rendering.md b/src/Rendering.md index 5fa9f3bf..24ed6b41 100644 --- a/src/Rendering.md +++ b/src/Rendering.md @@ -1,15 +1,27 @@ # Rendering overview -::: tip TERMINOLOGY +## Terminology -All references to a "dot" are meant as dots (one 4 MiHz time unit). -Dots remain the same regardless of whether the CPU is in [double speed](<#FF4D — KEY1 (CGB Mode only): Prepare speed switch>). +The entire frame is not drawn atomically; instead, the image is drawn by the **PPU** (Pixel-Processing Unit) progressively, **directly to the screen**. +A frame consists of 154 **scanlines**; during the first 144, the screen is drawn top to bottom, left to right. + +The main implication of this rendering process is the existence of **raster effects**: modifying some rendering parameters in the middle of rendering. +The most famous raster effect is modifying the [scrolling registers](<#LCD Position and Scrolling>) between scanlines to create a ["wavy" effect](https://gbdev.io/guides/deadcscroll#effects). + +A "**dot**" = one 222 Hz (≅ 4.194 MHz) time unit. +Dots remain the same regardless of whether the CPU is in [double speed](<#FF4D — KEY1 (CGB Mode only): Prepare speed switch>), so there are 4 dots per single-speed CPU cycle, and 2 per double-speed CPU cycle. + +::: tip + +Note that a frame is not exactly one 60th of a second: the Game Boy runs slightly slower than 60 Hz, as one frame takes ~16.74 ms instead of ~16.67 (the error is 0.45%). ::: +## PPU modes +
-The following diagram shows how a Game Boy frame is decomposed: +During a frame, the Game Boy's PPU cycles between four modes as follows:
@@ -17,19 +29,10 @@ The following diagram shows how a Game Boy frame is decomposed:
-TODO: high-level description of the above... and implications ("raster effects") - -## PPU modes - -The PPU operates on a 222 Hz = 4.194 MHz clock, called the "dot clock". -An 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. - While the PPU is accessing some video-related memory, [that memory is inaccessible to the CPU](<#Accessing VRAM and OAM>) (writes are ignored, and reads return garbage values, usually $FF). Mode | Action | Duration | Accessible video memory ------|--------------------------------------------|--------------------------------------|------------------------- +----:|--------------------------------------------|--------------------------------------|------------------------- 2 | Searching for OBJs which overlap this line | 80 dots | VRAM, CGB palettes 3 | Sending pixels to the LCD | Between 172 and 289 dots, see below | None 0 | Waiting until the end of the scanline | 376 - mode 3's duration | VRAM, OAM, CGB palettes @@ -37,6 +40,8 @@ Mode | Action | Duration ## Mode 3 length +During Mode 3, by default the PPU outputs one pixel to the screen per dot; the screen is 160 pixels wide, so the minimum Mode 3 length is 160 + 12[^first12] = 172 dots. + Unlike most game consoles, the Game Boy does not always output pixels steadily[^crt]: some features cause the rendering process to stall for a couple dots. Any extra time spent stalling *lengthens* Mode 3; but since scanlines last for a fixed number of dots, Mode 0 is therefore shortened by that same amount of time. @@ -46,9 +51,7 @@ Three things can cause Mode 3 "penalties": - **Window**: After the last non-window pixel is emitted, a 6-dot penalty is incurred while the BG fetcher is being set up for the window. - **Objects**: Each object drawn during the scanline (even partially) incurs a 6- to 11-dot penalty ([see below](<#OBJ penalty algorithm>)). -On DMG and GBC in DMG mode, mid-scanline writes to [`BGP`](<#FF47 — BGP (Non-CGB Mode only): BG palette data>) -allow observing this behavior, as the delay from drawing an OBJ shifts the -write's effect to the left by that many dots. +On DMG and GBC in DMG mode, mid-scanline writes to [`BGP`](<#FF47 — BGP (Non-CGB Mode only): BG palette data>) allow observing this behavior precisely, as any delay shifts the write's effect to the left by that many dots. ### OBJ penalty algorithm @@ -63,6 +66,8 @@ Only the OBJ's leftmost pixel matters here, transparent or not; it is designated **Exception**: an OBJ with an OAM X position of 0 (thus, completely off the left side of the screen) always incurs a 11-cycle penalty, regardless of `SCX`. -TODO: a diagram of some examples would probably help this be much clearer! \>_\< +TODO: a diagram of some examples would probably help this be much clearer! + +[^first12]: The 12 extra cycles come from two tile fetches at the beginning of Mode 3. One is the first tile in the scanline (the one that gets shifted by `SCX` % 8 pixels), the other is simply discarded. [^crt]: The Game Boy can afford to "take pauses", because it writes to a LCD it fully controls; by contrast, home consoles like the NES or SNES are on a schedule imposed by the screen they are hooked up to. Taking pauses arguably simplified the PPU's design while allowing greater flexibility to game developers. From 5bd1b38ec9419e242eba8f9160ecce6be526dee7 Mon Sep 17 00:00:00 2001 From: Antonio Vivace Date: Wed, 13 Sep 2023 15:21:54 +0200 Subject: [PATCH 5/8] Update src/Rendering.md Co-authored-by: Eldred Habert --- src/Rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rendering.md b/src/Rendering.md index 24ed6b41..01e29130 100644 --- a/src/Rendering.md +++ b/src/Rendering.md @@ -40,7 +40,7 @@ Mode | Action | Duration ## Mode 3 length -During Mode 3, by default the PPU outputs one pixel to the screen per dot; the screen is 160 pixels wide, so the minimum Mode 3 length is 160 + 12[^first12] = 172 dots. +During Mode 3, by default the PPU outputs one pixel to the screen per dot, from left to right; the screen is 160 pixels wide, so the minimum Mode 3 length is 160 + 12[^first12] = 172 dots. Unlike most game consoles, the Game Boy does not always output pixels steadily[^crt]: some features cause the rendering process to stall for a couple dots. Any extra time spent stalling *lengthens* Mode 3; but since scanlines last for a fixed number of dots, Mode 0 is therefore shortened by that same amount of time. From 4316faaa0493cfc29999de565d0a29afdc79ef69 Mon Sep 17 00:00:00 2001 From: Antonio Vivace Date: Wed, 13 Sep 2023 15:45:00 +0200 Subject: [PATCH 6/8] Update src/Rendering.md Co-authored-by: Eldred Habert --- src/Rendering.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rendering.md b/src/Rendering.md index 01e29130..6e5eda58 100644 --- a/src/Rendering.md +++ b/src/Rendering.md @@ -66,7 +66,6 @@ Only the OBJ's leftmost pixel matters here, transparent or not; it is designated **Exception**: an OBJ with an OAM X position of 0 (thus, completely off the left side of the screen) always incurs a 11-cycle penalty, regardless of `SCX`. -TODO: a diagram of some examples would probably help this be much clearer! [^first12]: The 12 extra cycles come from two tile fetches at the beginning of Mode 3. One is the first tile in the scanline (the one that gets shifted by `SCX` % 8 pixels), the other is simply discarded. From eed64bbc816ad5c38d6a119e9fdebd600a8b2e79 Mon Sep 17 00:00:00 2001 From: Antonio Vivace Date: Wed, 13 Sep 2023 15:52:13 +0200 Subject: [PATCH 7/8] Update src/Audio.md Co-authored-by: Eldred Habert --- src/Audio.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Audio.md b/src/Audio.md index 5d3d9c19..effd4259 100644 --- a/src/Audio.md +++ b/src/Audio.md @@ -36,7 +36,7 @@ The speaker merges back the two channels, losing the stereo aspect entirely. The Game Boy's sound chip is called the APU. -The APU runs off the same master clock as the rest of the Game Boy, which is to say, it is fully synced with the CPU and [PPU](<#Graphics Overview>). +The APU runs off the same master clock as the rest of the Game Boy, which is to say, it is fully synced with the CPU and [PPU](<#Rendering Overview>). This also means that the APU runs about 2.4% faster on the SGB1, increasing frequencies by as much and thus sounding slightly higher-pitched. The SGB2 rectifies this issue. From 07574c5e8554d15395b3c9347d850fa9b83d85a5 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Sun, 24 Sep 2023 21:31:35 +0200 Subject: [PATCH 8/8] Address reviews --- src/Rendering.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Rendering.md b/src/Rendering.md index 6e5eda58..3a1ec08c 100644 --- a/src/Rendering.md +++ b/src/Rendering.md @@ -58,9 +58,9 @@ On DMG and GBC in DMG mode, mid-scanline writes to [`BGP`](<#FF47 — BGP (Non-C Only the OBJ's leftmost pixel matters here, transparent or not; it is designated as "The Pixel" in the following. 1. Determine the tile (background or window) that The Pixel is within. (This is affected by horizontal scrolling and/or the window!) -2. If that tile has **not** been considered by a previous OBJ yet: - 1. Count how many of that tile's pixels are to the right of The Pixel. - 2. Subtract 3. +2. If that tile has **not** been considered by a previous OBJ yet[^order]: + 1. Count how many of that tile's pixels are strictly to the right of The Pixel. + 2. Subtract 2. 3. Incur this many dots of penalty, or zero if negative (from waiting for the BG fetch to finish). 3. Incur a flat, 6-dot penalty (from fetching the OBJ's tile). @@ -70,3 +70,5 @@ Only the OBJ's leftmost pixel matters here, transparent or not; it is designated [^first12]: The 12 extra cycles come from two tile fetches at the beginning of Mode 3. One is the first tile in the scanline (the one that gets shifted by `SCX` % 8 pixels), the other is simply discarded. [^crt]: The Game Boy can afford to "take pauses", because it writes to a LCD it fully controls; by contrast, home consoles like the NES or SNES are on a schedule imposed by the screen they are hooked up to. Taking pauses arguably simplified the PPU's design while allowing greater flexibility to game developers. + +[^order]: Since pixels are emitted from left to right, OBJs overlapping the scanline are considered from [leftmost](<#Byte 1 — X Position>) to rightmost, with ties broken by their index / OAM address (lowest first).