Zal0 / ZGB

Game Boy / Color engine with lots of features

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Occasional Corrupt Map Tiles

DonaldHays opened this issue · comments

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

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

commented

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

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:

  • Ensure all interrupts return during Mode 0; it's important to take the time it will take to return into account, since functions may expect to get the full 20 cycles of Mode 2. To be the most compatible, the start of Mode 0 should be aimed for, see the snip below
  • Use di around the loop. This is dangerous, as it delays interrupts, and may significantly offset them, causing more weird and hard-to-debug breakage. I strongly discourage that option despite it seeming much simpler.

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
#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

commented

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