I wanted to cover the top part of the screen to use as a status bar for a game. My idea was to use the Game Boy's window feature to do so. But I ran into a problem: The window's size is fixed to be the entire size of the screen, and while it can be lowered so that the window slides down towards the bottom of the screen revealing the background towards the top, the reverse can't be done.
So I thought "That's ok, I'll use a horizontal interrupt to move the window off screen at a specified scanline." I was thinking that if during vBlank the window Y coordinate was set to 0, it would only cover the screen up to the specified scanline, and then disappear for the rest of the screen draw, resulting in a status bar fixed at the top of the screen (think Zelda: Oracle of Seasons). But this isn't what happens at all! I'll show you what happens:
This is what happens if I use an LYC interrupt to set the WY register to scanline (decimal) 20. It works as intended. There is no reset of the WY register during vBlank in this screenshot.
Now, this is what I get when I set WY to 20 at scanline 20 using an LYC interrupt, and then during vBlank set WY to zero. The window covers up the screen the entire time? That doesn't make sense...
...unless the Game Boy has a hardware limitation that the window can only be moved once per frame. Is that the case?
(It could be a bug in my code so I'll post it below:
; This code is copied to &A000 and executed there.
; The LYC register contains 20.
LYCProc:
push af
gb_in <ly
gb_out <wy
pop af
reti
LYCProc_End:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This code is copied to &FF80 and executed there during vblank.
; (The stack was moved to &BFFF early on in the program)
DMACopy:
push af
ld a,>GBSpriteCache ;high byte of gba sprite cache
gb_out <dma ;start the transfer
ld a,&28 ;delay
DMACopyWait: ;wait for DMA to finish before returning program counter to ROM
dec a
jr nz,DMACopyWait
xor a
gb_out <wy ;reset the window back to cover the screen
pop af
reti
DMACopyEnd:
EDIT: I figured out a way to do what I want but it makes no sense. I ported my code to a minimal example and used these interrupts:
VBlankProc: ;&0040 jumps here
push af
xor a ;ld a,0. Move window back to cover the whole screen
ld (&ff4b),a ;this is WX, yet it acts like WY!
pop af
reti
VBlankProc_End:
LYCProc: ;&0048 jumps here, triggers at scanline #32
push af
ld a,&ff ;move window off screen
ld (&ff4b),a ;this is WX, yet it acts like WY!
pop af
reti
LYCProc_End:
And this is the result (screenshot is from BGB but VisualBoyAdvance does the same thing)
This is what I was trying to do earlier! So now I'm even more confused than before. Why does altering the window X register do what I thought Window Y would do?