Page 1 of 1

Mirroring or scrolling issue?

Posted: Sat Jul 30, 2011 9:58 pm
by ehguacho
Hi everyone! I'm having some problems while implementing scrolling. Look at this cap:

Image

I guess the problem might be in the way I'm handling the Name Tables. I'm using two variables to hold the directions for VRAM access: one for the "general" VRAM access (wich I'll call VRAMAddress from now on) and other for rendering (wich I'll call LoopyV from now on). Both are setted in the second write to $2006 register. LoopyV is modified in rendering while VRAMAddress doesn't, so if a value is written to a Name Table via $2007 register during HBlank, this will be written in the address pointed by VRAMAddress (and not in the address pointed by LoopyV). Is this correct? I'm asking just to be sure, because I can't figure out why the graphics are shown like this (i mean like the graphics in the imagen previously shown). Just in case, this is the way I'm handling mirroring and $2005/6 registers write:

Code: Select all

void WriteRegister2005(byte Value)
{
    if(!VRAMLatchHigh)
    {
        LoopyT &= (~0x001F);
        LoopyT |= (Value >> 3);
    }
    else
    {
        LoopyT &= (~0x73E0);
        LoopyT |= ((Value & 0xF8) << 2);
        LoopyT |= ((Value & 0x7) << 12);
    }

    VRAMLatchHigh = (!VRAMLatchHigh);
}

Code: Select all

void WriteRegister2006(byte Value)
{
    if(!VRAMLatchHigh)
    {
        LoopyT &= (~0xFF00);
        LoopyT |= ((Value & 0x3F) << 8);
    }
    else
    {
        LoopyT &= (~0x00FF);
        LoopyT |= Value;
        VRAMAddress = LoopyT;
        LoopyV = LoopyT;
    }

    VRAMLatchHigh = (!VRAMLatchHigh);
}

Code: Select all

void WriteRegister2007(byte Value)
{
    // Mappers
    if(VRAMAddress < 0x2000)
    {
        // Nothing yet. To be writen while implementing mappers
    }

    // Name tables
    else if((VRAMAddress >= 0x2000) && (VRAMAddress < 0x3F00))
    {
        if(Mirroring == MIRRORING_HORIZONTAL)
        {
            switch(VRAMAddress & 0x2C00)
            {
                case 0x2000: VRAM[VRAMAddress] = VRAM[VRAMAddress + 0x400] = Value; break;
                case 0x2400: VRAM[VRAMAddress] = VRAM[VRAMAddress - 0x400] = Value; break;
                case 0x2800: VRAM[VRAMAddress] = VRAM[VRAMAddress + 0x400] = Value; break;
                case 0x2C00: VRAM[VRAMAddress] = VRAM[VRAMAddress - 0x400] = Value; break;
            }
        }
        else if(Mirroring == MIRRORING_VERTICAL)
        {
            switch(VRAMAddress & 0x2C00)
            {
                case 0x2000: VRAM[VRAMAddress] = VRAM[VRAMAddress + 0x800] = Value; break;
                case 0x2400: VRAM[VRAMAddress] = VRAM[VRAMAddress + 0x800] = Value; break;
                case 0x2800: VRAM[VRAMAddress] = VRAM[VRAMAddress - 0x800] = Value; break;
                case 0x2C00: VRAM[VRAMAddress] = VRAM[VRAMAddress - 0x800] = Value; break;
            }
        }
    }

    // Palette
    else if((VRAMAddress >= 0x3F00) && (VRAMAddress < 0x3F20))
    {
        VRAM[VRAMAddress] = Value & 0x3F;
        if((VRAMAddress == 0x3F00) || (VRAMAddress == 0x3F10)) VRAM[0x3F00] = VRAM[0x3F10] = Value & 0x3F;
        if((VRAMAddress == 0x3F04) || (VRAMAddress == 0x3F14)) VRAM[0x3F04] = VRAM[0x3F14] = Value & 0x3F;
        if((VRAMAddress == 0x3F08) || (VRAMAddress == 0x3F18)) VRAM[0x3F08] = VRAM[0x3F18] = Value & 0x3F;
        if((VRAMAddress == 0x3F0C) || (VRAMAddress == 0x3F1C)) VRAM[0x3F0C] = VRAM[0x3F1C] = Value & 0x3F;
    }
}

Code: Select all

byte ReadRegister2007(word Address)
{
    byte Value = 0;

    // Address out of palette range -> Delayed read
    if (Address < 0x3F00)
    {
        Value = VRAMBuffer;

        // Mappers
        if (VRAMAddress < 0x2000)
        {
            // Nothing yet. To be writen while implementing mappers
        }

        // Name tables
        else if (VRAMAddress >= 0x2000) VRAMBuffer = VRAM[VRAMAddress];
    }

    // Address in palette range -> No delayed read
    else Value = VRAM[Address];

    return Value;
}
Sprite #0 hit flag is implemented correctly, or that's what I think since Mario starts running all across the screen a few seconds after the main screen have been shown.

At this point, I don't know if this is a mirroring issue or a scrolling issue.

Any hint will be gratefully thanked. Thank you all in advance.

Posted: Sat Jul 30, 2011 10:56 pm
by MottZilla
Writes to $2000, $2005, $2006 all change "LoopyT". The second write to **** $2006 will change LoopyV.

SMB works by waiting for Sprite Hit Zero to occur and then timing writes to I think $2000, and $2005 or $2006. These occur at some time at the end of the scanline so the changes will alter the next line rendered.

Remember that PPU Access through $2006 is the same register as the one used in Scrolling/Rendering. There isn't a different register for CPU PPU access via $2006.

Be sure to check that $2000, $2005, and $2006 all effect LoopyT and V correctly.

Posted: Sat Jul 30, 2011 11:51 pm
by ehguacho
thanks for your response, but now I'm confused. According to Disch's explanation for Loopy's Doc (http://nesdev.com/bbs/viewtopic.php?t=664) the only register that affects LoopyV is $2006 during its second write.

From Disch's topic:
LoopyT does NOTHING... aside from reload LoopyV at certain times. If you want to actually affect where the PPU draws from (ie, change the scrolling)... or change the address so you can write to $2007... LoopyV is what you need to change. LoopyV does it all. This is why you can't use just $2005 to change the vertical scroll mid-frame (since $2005 never changes LoopyV) and why clever alternating $2006/$2005 writes are required...since the second $2006 write is the only way to set LoopyV aside from the scroll reset at frame start.
Anyway I think I'm having some CPU bugs. I've downloaded Bomberman and Elevator Action to test them on my emu and both have problems. Bomberman freezes out on the main screen, the sprite used for game mode selection doesn't even get shown. Elevetor Action crashes when trying to read to address 0x0201, which is supposed to be 0xEA (NOP) according to a debug I've made with FCEUX. So I might debug my 6502's core now :(

Sorry about the post and for making you waste your time. Didn't know that I was having CPU bugs, I've matched correctly NESTEST's golden log.

Posted: Sun Jul 31, 2011 2:04 am
by ehguacho
Nope! My core is fine. I was just going into NMI on scanline 242 (start counting from 0) when might be going on scanline 241 (the idle scanline). By the way, is in the idle scanline where I should be raising the VBL flag?

Posted: Sun Jul 31, 2011 3:33 am
by NickMass
The Super Mario Bros. title screen needs accurate VRAM read/write buffer emulation and you have made a couple of small mistakes with it.
In your ReadRegister2007()

Code: Select all

 
// Address in palette range -> No delayed read 
else Value = VRAM[Address];
needs to be changed to be something like

Code: Select all

// Address in palette range -> No delayed read 
else
{
Value = PaletteRAM[Address];
VRAMBuffer = VRAM[Address];
}
your WriteRegister2007() would also have to be changed accordingly, but the idea is that the Palette RAM is stored completely separate from the VRAM, I suggest you work towards passing the vram_access.nes test rom found inside this archive http://www.slack.net/~ant/nes-tests/bla ... _tests.zip it should at least reduce the problems you are seeing, though it wouldn't surprise me if things broke once the game started scrolling.

Posted: Sun Jul 31, 2011 9:53 am
by ehguacho
tried those test already, but they all get stucked and shows nothing on screen, so there's something going wrong in my emulator :\

Posted: Sun Jul 31, 2011 11:37 am
by MottZilla
Sorry my post was incorrect, I should have checked for you. LoopyV = LoopyT only at two events. Rendering Start and a second write to $2006.

Be sure to implement PPU Read access through $2007 with the buffer! Games expect this to be true! When you read $2007, a buffered value comes out and then the value at the current LoopyV address is loaded into the buffer and the LoopyV address increases.

Check regular Mario Bros (not Super) and if it doesn't play correctly it should show you that your PPU reading isn't working right. It relys on it for collision detection.

Posted: Sun Jul 31, 2011 6:57 pm
by ehguacho
Mario works fine :)