gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > mode switching

#176695 - GLaDOS - Thu Sep 15, 2011 5:16 am

Most of my game is in mode 0, but I want to use mode 3 for the victory screen. However, I can't get mode switching to work. No matter what I do, I get random garbage unless the game starts in mode 3 and never switches away. If I start in mode 0 and later switch to mode 3, it refuses to display anything.

Is this a problem with the emulator (VBA-M)? Is there some caveat to mode switching? What am I doing wrong?

Code:
#include "GBA/mylib.h"
#include "GBA/bgmanager.h"
#include "GBA/spritemanager.h"
#include "physics.h"
#include "input.h"

#include "GBA/dma.h"
#include "GBA/victory.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setVictoryScreen()
{
    REG_DISPCNT = Mode(3) | BG2_ENABLE;

    DMA[3].src = VICTORY;
    DMA[3].dst = (vu16*)(0x06000000); //Will actually overwrite 96kb worth of memory, not just 1 block
    DMA[3].cnt = VICTORY_SIZE/2 | DMA_ON | DMA_NOW | DMA_32 | DMA_SOURCE_INCREMENT | DMA_DESTINATION_INCREMENT;

    REG_DISPCNT = Mode(3) | BG2_ENABLE;
}

int main()
{
    //REG_DISPCNT = Mode(3) | BG2_ENABLE;
    //REG_DISPCNT = Mode(0) | BG0_ENABLE | SPRITES_ENABLE | SPRITE_INDEXING_1D | WIN0_ENABLE;
    REG_DISPCNT = Mode(0) | BG0_ENABLE | SPRITES_ENABLE | SPRITE_INDEXING_1D;
    //REG_DISPCNT = Mode(1) | BG0_ENABLE | BG1_ENABLE | SPRITES_ENABLE | SPRITE_INDEXING_1D;

    //Set Background Control Register
    BG_Init();
    LoadSpriteTiles();
    HideSprites(0);   //Must be called since initially, uninitialized sprites are visible

    newGame();

    //Start game loop
    //while (!victory())
    while (false)
    {
        update(pollLR(), pollUD(), pollA(), pressedZ(), pressedX(), pollUp());
        if (pressedEnter()) {restartLevel();}
        if (pressedEsc()) {newGame();}

        while(SCANLINECOUNTER >= 160);
        while(SCANLINECOUNTER < 160);
        draw();
    }

    while(SCANLINECOUNTER >= 160);
    while(SCANLINECOUNTER < 160);
    setVictoryScreen();
    while (1) {}
}

#176696 - Dwedit - Thu Sep 15, 2011 5:37 am

Are you doing any other writes to DISPCNT through DMA or interrupts?


Edit: Use the "MODE_0" or "MODE_3" macro, I have no idea what the "Mode()" macro is. The Mode() macro doesn't even exist in Libgba.
MODE_0 and MODE_3 are defined as 0 and 3.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."


Last edited by Dwedit on Thu Sep 15, 2011 5:41 am; edited 1 time in total

#176697 - GLaDOS - Thu Sep 15, 2011 5:40 am

Those are the only writes to DISPCNT and I don't have any interrupts at all. I suppose I could have messed up somewhere and accidently written over the control registers with a DMA but I doubt it.

Also, Mode(x) is this
inline u16 Mode(u16 mode) {return mode;}


Edit: Nevermind I figured out the problem. For some reason, REG_DISPCNT is not declared volatile in the set of definitions I was using. Anyway, it's working now.

#176698 - ant512 - Thu Sep 15, 2011 9:05 am

GLaDOS wrote:
Also, Mode(x) is this
inline u16 Mode(u16 mode) {return mode;}


Interesting - the function doesn't do anything, and as it's inlined it'll just disappear and leave the parameter on its own. Is that a way to have the code self-document the magic numbers for mode without using defines?

#176699 - GLaDOS - Thu Sep 15, 2011 1:43 pm

Yes. I figured it has the advantage over a plain macro in that it forces the correct type.

#176700 - ant512 - Thu Sep 15, 2011 2:23 pm

Neat trick!

#176703 - Dwedit - Thu Sep 15, 2011 5:24 pm

You need to declare inline methods as "static", otherwise the compiler will also export a full copy into the object code.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#176704 - ant512 - Thu Sep 15, 2011 6:27 pm

Edit: Never mind, brain not working.

#176707 - Miked0801 - Thu Sep 15, 2011 10:22 pm

Keep in mind that mode changes take a few scanlines to complete. If you are not in VBlank or have the screen in a state where it won't show, you will get visual artifacting.

#176709 - GLaDOS - Thu Sep 15, 2011 10:50 pm

I'm surprised you're allowed to change it outside of vblank at all. I would have figured that the hardware locked it or something.

#176710 - DiscoStew - Fri Sep 16, 2011 5:01 am

I remember having done something like this a long time ago when I began making a BG library, where I could switch modes outside of the VBlank without garbage/artifacts showing up. I'd have to go digging through all my old CD backups to find the actual code, but if I remember how it did it, I did this...

I had every available layer active from the get-go, but setting all layers not in actual use to have both their character and screen base blocks set to 0 (meaning the beginning of VRAM), and reserving a certain amount of VRAM at the beginning to be completely zeroed out (I think about 2KB as that is the minimum of a 256x256 map). Essentially, the layers would be in use, but since all the data being used is set to 0, it would result in tiles filled with index 0 (being the transparent pixel), and the map of the layer using the first tile, which would be transparent already.

If I find my code, I'll post it. It might have been only for non-bitmap modes, but I'm not sure.
_________________
DS - It's all about DiscoStew

#176715 - Miked0801 - Fri Sep 16, 2011 5:53 pm

We did this for Digimon Racing. The ground was done as a Mode7 (mode1 or 2) while we had the sky done in Mode 0 for more chars. The interface between the two was covered up with sprites or made a color that would not show corruption.

I honestly don't remember if we shipped this way or swapped to something else though, because we also had to support interruptable multiplayer communication via the propriatary RFU unit.