Zal0 / ZGB

Game Boy / Color engine with lots of features
MIT License
706 stars 50 forks source link

Occasional Corrupt Map Tiles #1

Closed DonaldHays closed 4 years ago

DonaldHays commented 7 years ago

I'm not programming using this engine myself, but there's a visual issue I've seen in Super Princess' 2092 Exodus and Luna, so I suspect it's a bug in the engine, and so I thought I would file a ticket.

Sometimes when scrolling the engine appears to incorrectly decode or assign a map tile, resulting in a corrupt appearance. The tile remains in its incorrect state as long as it remains on-screen. If I scroll the tile off-screen and then back on-screen the issue goes away. Unfortunately, I don't have a reliable way to force the issue to happen. It just happens somewhat rarely seemingly at random.

I have attached photos of the bug expressing twice in Luna. Look towards the top-left corner. Again, I saw the issue once or twice in Super Princess' 2092 Exodus, but I lack any screenshots of the issue there.

zgb_1 zgb_2

DonaldHays commented 7 years ago

Because the first two shots appeared to show palette-decode errors, here's another shot where the tile data is definitely wrong. Look at the mountain to the right of the hero. When I scroll that off and back on it fixes to a continuous upward slope.

img_0520

Zal0 commented 7 years ago

So after a bit of investigation I found out the function set_bkg_tiles can fail writing into vram sometimes because of the vblank interruption that I am using. I have coded a new function SetTile faster than set_bkg for one tile and that also checks out on exit if the stat_reg marks vram in modes 0 or 1 (writable) and otherwise it tries it again. (more info here)

Changes are in this commit

ISSOtm commented 4 years ago

A game using ZGB (which is apparently a recent version) still exhibits a form of this bug. The game has the timer interrupt enabled, and that interrupt sometimes returns into the loop check after it passed but before the write occurred.

Here is what happens:

.waitVRAM
    ldh a, [rSTAT]   ; <-- STAT is read, indicates, say, Mode 0
    and 2
    jr nz, .waitVRAM
.waitVRAM
    ldh a, [rSTAT]
    and 2            ; A is ANDed, and indicates the mode is suitable for writing (which it is)
    jr nz, .waitVRAM

(An interrupt occurs, and returns during Mode 3)

.waitVRAM
    ldh a, [rSTAT]
    and 2
    jr nz, .waitVRAM ; Z is set, so the loop exits, but we are during Mode 3!

There are two ways to fix this:

For the first fix, this should be the end of each interrupt handler (and since GBDK centralizes all handlers, it's easier to apply)

.waitVRAM
    ldh a, [rSTAT]
    and 2
    jr z, .waitVRAM
.waitVRAMBeginning
    ldh a, [rSTAT]
    and 2
    jr nz, .waitVRAMBeginning
    ; This is only reached during the beginning of VBlank or HBlank
    pop af
    reti ; Assuming interrupts are disabled during handlers, which they should at least during these loops
jchristof commented 4 years ago
#include "Banks/SetBank2.h"
#include <gb/gb.h>

const unsigned char tile[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

void Start_StateGame(){}

void Update_StateGame()
{
    set_win_data(0, 1, tile);
}

Run this state with the current ZGB engine. In BGB's exceptions tab check the options for "Inaccessible VRAM:Break on access" After a moment, BGB will halt on a VRAM access during mode 3

Zal0 commented 4 years ago

Finally fixed after updating gbdk 2020 Thanks a lot, everyone!