Window behavior
FF4A–FF4B — WY, WX: Window Y position, X position plus 7
These two registers specify the on-screen coordinates of the Window’s top-left pixel.
The Window is visible (if enabled) when WX and WY are in the range [0; 166] and [0; 143] respectively.
Values WX=7, WY=0 place the Window at the top left of the screen, completely covering the background.
Window mid-frame behavior
While the Window should work as just mentioned, writing to WX, WY etc. mid-frame displays more articulated behavior.
There are several aspects of the window that respond differently to various mid-frame interactions; the tl;dr is this:
- For the least glitchy results, only write to
WX,WY, andLCDCduring VBlank (possibly in your VBlank interrupt handler); if mid-frame writes are required, prefer writing during HBlank. - If intending to hide the Window for part of the screen (e.g. to have a status bar at the top of the screen instead of the bottom), hide it by setting
WXto a high value rather than writing toLCDC.
Window rendering criteria
The PPU keeps track of a “Y condition” throughout a frame.
- On each VBlank, the Y condition is cleared (becomes false).
- At the beginning of each scanline, if the value of
WYis equal toLY, the Y condition becomes true (and remains so for subsequent scanlines).
Note
On GBC, clearing the Window enable bit in LCDC resets the Y condition; WY must be set to LY or greater for the Window to display again in the current frame.
Additionally, the PPU maintains a counter, initialized to 0 at the beginning of each scanline. The counter is incremented for each pixel rendered; however, it also increments 7 times before the first pixel is actually rendered (this covers pixels discarded during the initial “fine scroll” adjustment).
When this counter is equal to WX, if the Y condition is true and the Window enable bit is set in LCDC, background rendering is reset, beginning anew from the active row of the Window’s tilemap.
The coordinate of the active Window row is then incremented.
-
This process can happen more than once per scanline, making the Window’s “tilemap Y coordinate” increase more than once in the scanline. (This is demonstrated by the TODO test ROM.)
However, this requires “disabling” the Window by briefly clearing its enable bit from
LCDCfirst. -
If this process doesn’t happen, the Window’s “tilemap Y coordinate” does not increase; so, if the Window is hidden (by any means) on a given scanline, the row of pixels rendered the next time it’s shown will be the same as if it had not been hidden in the first place, producing a sort of vertical striped stretching:

-
If
WXis equal to 0, the Window is switched to before the initial “fine scroll” adjustment, causing it to be shifted left by pixels. -
On monochrome systems,
WX= 166 (which would normally show a single Window pixel, along the right edge of the screen) exhibits a bug: the Window spans the entire screen, but offset vertically by one scanline. -
On monochrome systems, if the Window is disabled via
LCDC, but the other conditions are met and it would have started rendering exactly on a BG tile boundary, then where it would have started rendering, a single pixel with ID 0 (i.e. drawn as the first entry in the BG palette) is inserted; this offsets the remainder of the scanline.1
-
This was discovered as affecting the game Star Trek 25th anniversary; more information and a test ROM are available in this thread. ↩