[My emulator] Graphics glitches - SuperMarioBros

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
L-proger
Posts: 5
Joined: Sat Sep 24, 2011 3:56 am
Location: Russia
Contact:

[My emulator] Graphics glitches - SuperMarioBros

Post by L-proger » Sat Sep 24, 2011 4:34 am

Hi all!

Sorry, if my English isn't good : )


I'm newbie in emulation! Seven days ago i started to write my first emulator (of course NES ^__^). I had no imagination about how to write it or how it should works : ) Anyway, after 7 days (now) i can run few simple games, like SuperMarioBrothers, Excitebike, IceClimber and few more with 0 mapper.

Before implementing various mappers i want to fix all emu glitches. look at screen below:

Image

This is the only one GFX bug in Mario, everything else looks really good. Second day i can't figure out why some parts of background in startup menu are missing and few of them are replaced with "red cloud" sprites : \

First thing i thought is that i did something wrong in background rendering code! I checked it 10 times - everything looks fine.

Then i used FCEUX to find one my corrupted tile address in VRAM and tiles ID in PatternTable!

That tile:
Image

Here should be renderer tile with ID == 0x46 but instead rendered tile 0x24 (transparent) o_O

FCEUX says, that this tile ID is placed in 0x21A5 VRAM address (NameTable0). I made a few hooks to watch - what and when is writing at that address - and.. surprise! Game exactly writes value 0x24 (transparent)!!! It means, that i render everything correcly but game writes wrong values in NameTable0!

And question is: "why?" I think, that this is some kind of GFX debugging, implemented by Mario game programmers ) It's just a reaction on something made wrong. So, maybe somebody know what exactly can allow game to produce such glitches?

Big thanx to all! : )

User avatar
Memblers
Site Admin
Posts: 3877
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers » Sat Sep 24, 2011 5:22 am

SMB is reading some data from CHR-ROM there, isn't it? Most games don't do that. That might be where the problem is.

User avatar
L-proger
Posts: 5
Joined: Sat Sep 24, 2011 3:56 am
Location: Russia
Contact:

Post by L-proger » Sat Sep 24, 2011 5:40 am

Thanks for reply!

Yep, SMB is reading something from CHR-ROM, but reading is already implemented! : \

Maybe that can help: i tried to run CPU/PPU test ROMs on my EMU, but i can't see any sprites rendered! (only black screen). I tried so many tricks, so i don't have ideas now on what is wrong with emu. Hmm.

(i implemented CHR-RAM writes too, so i expected that test ROMs would work fine, but they don't)

tepples
Posts: 22050
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Sat Sep 24, 2011 6:20 am

Are you making sure that data read back from CHR ROM is delayed by one read? For example, point the VRAM address at $1F00, where $1F00 contains "HELLO WORLD",0, and reading $2007 will read back a byte of garbage before "HELLO".

User avatar
L-proger
Posts: 5
Joined: Sat Sep 24, 2011 3:56 am
Location: Russia
Contact:

Post by L-proger » Sat Sep 24, 2011 7:12 am

tepples wrote:Are you making sure that data read back from CHR ROM is delayed by one read? For example, point the VRAM address at $1F00, where $1F00 contains "HELLO WORLD",0, and reading $2007 will read back a byte of garbage before "HELLO".
OMG! O____O Just fixed! Thanx!!!

Look what i did:

Code: Select all

uint8 PPU::ReadVRAM()
{	
	uint8 val = 0;
	if(reg_vramAddress2 < 0x2000) //pattern table
	{
		val = pNes->pCart->ReadChrRom(reg_vramAddress2 - 1);
	}
              else if ....

              //blah blah blah

	reg_vramAddress2 += addressIncrement;
	return val;
}
I added "-1" to read address! Please, explain - why i should do this? I can't catch..

If i understand correctly:
To write some value to some address of VRAM, game should:

1. Write VRAM address in PPU "VRAM Address Register 2" - 2 writes by 8 bits, then combine them to 16 bit address;

2. Well, write data and then increment pointer by 1 or 32 bytes (depends on 0x4 bit of "PPU Control Register 1".

And same thing to read data! Specify pointer to data and read it... hmm
What's wrong? What I'm missing? : )

3gengames
Formerly 65024U
Posts: 2277
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames » Sat Sep 24, 2011 7:24 am

Add a read buffer that stuff go directly to the CPU when you read, as when you reset the address, the buffer to read gets filled with I believe the last write to the PPU. See the bottom of the wiki:

http://wiki.nesdev.com/w/index.php/PPU_registers

I'm not sure what the internal buffer is, my guess is the last write, but let somebody else who really knows answer that. And also, keep in mind it's not for the palette range, those are directly read back, no buffer.

User avatar
L-proger
Posts: 5
Joined: Sat Sep 24, 2011 3:56 am
Location: Russia
Contact:

Post by L-proger » Sat Sep 24, 2011 7:32 am

>I'm not sure what the internal buffer is
this is just a 1 byte value of previous read operation in address range 0-$3EFF, as i understand!

I just made correct delayed read, not just "-1". But i want to understand, why not read it immediately? Why CPU needs that delay? And why then reading of $3F00-$3FFF should not be delayed??

btw - cool, now i understand why documents says, that first read of VRAM would return garbage =)

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Sat Sep 24, 2011 8:32 am

L-proger wrote:But i want to understand, why not read it immediately? Why CPU needs that delay? And why then reading of $3F00-$3FFF should not be delayed??
Most of us don't know about the hardware details, so I'm not sure you'll get a definitive answer to this. What we DO know though is that the pattern and name tables, which are affected by the delay, are memory chips separate from the PPU chip, but the palette RAM is internal to the PPU, so it doesn't have to make any external memory accesses to fetch data from it.

User avatar
L-proger
Posts: 5
Joined: Sat Sep 24, 2011 3:56 am
Location: Russia
Contact:

Post by L-proger » Sat Sep 24, 2011 8:35 am

tokumaru wrote:
L-proger wrote:But i want to understand, why not read it immediately? Why CPU needs that delay? And why then reading of $3F00-$3FFF should not be delayed??
Most of us don't know about the hardware details, so I'm not sure you'll get a definitive answer to this. What we DO know though is that the pattern and name tables, which are affected by the delay, are memory chips separate from the PPU chip, but the palette RAM is internal to the PPU, so it doesn't have to make any external memory accesses to fetch data from it.
Okay, i got it : )

Big thanks to all!! Many games now works correctly ^__^

tepples
Posts: 22050
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Sat Sep 24, 2011 9:28 am

It's probably because the PPU can't complete the read within the time that the CPU expects, so it reads to a buffer and then returns that buffer on the next read. On the Apple IIGS, the interface to wave RAM has the same delayed-read behavior.

Is it documented what mixing reads and writes leaves in the buffer? Or what mixing $0000-$3EFF reads and palette ($3F00-$3FFF) reads leaves? Or what rendering leaves?

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Sat Sep 24, 2011 9:39 am

tepples wrote:Is it documented what mixing reads and writes leaves in the buffer? Or what mixing $0000-$3EFF reads and palette ($3F00-$3FFF) reads leaves? Or what rendering leaves?
I'm a bit curious about those behaviors myself. They should be easy to test though, except for the rendering part...

Zelex
Posts: 268
Joined: Fri Apr 29, 2011 9:44 pm

Post by Zelex » Sat Sep 24, 2011 7:33 pm

I'm always amazed at some people here's ability to look at a picture and know precisely what is wrong at the code level. Dude, fraking amazing

User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Post by kevtris » Mon Sep 26, 2011 9:38 am

tepples wrote:It's probably because the PPU can't complete the read within the time that the CPU expects, so it reads to a buffer and then returns that buffer on the next read. On the Apple IIGS, the interface to wave RAM has the same delayed-read behavior.

Is it documented what mixing reads and writes leaves in the buffer? Or what mixing $0000-$3EFF reads and palette ($3F00-$3FFF) reads leaves? Or what rendering leaves?
I am not 100% sure what happens on mixed read/write, but the pointer is incremented after each. You can use reads to skip bytes to be written in chr space.

I tested this ages ago and I cannot remember if the buffer (which stores the read byte) is used or not to store the written value.

The reason reads are buffered is as described though: the chip has to perform a read, and it takes longer to do this than the CPU can wait, so you get to read the previous buffer value instead. The exception to this is the palette: Since it's on-chip, there is no delay. You read it back immediately.

Once you read the buffer, the PPU then fetches the next byte from CHR space.

As for reading memory UNDER the palette registers, you can do this thanks to the buffer. Reading from 3fxx will return palette data, and it will also read from 3fxx in PPU space (usually the nametables since they are mirrored from 2000-3fff). This can be used to read one byte at a time from external space at 3fxx.

To do this, you first read from i.e. 3f00 which returns the first palette entry, then you read from somewhere else (i.e. 1000h or 0000h, basically anywhere except 3f00). this will return the byte of data in the buffer, which was loaded from 3f00.

Then you read from 3f01 to load the buffer, then read from i.e. 1000h again to read the buffered byte. This process then continues until you've read the entire "hidden" address space.

How useful this is is debatable, but it will trip up many emulators if you wish to write emulator detection code for some reason (and it would make some dandy on-cart copy protection maybe if you were to map CHR ROM there in certain cases and then read it back discreetly later on).
/* this is a comment */

User avatar
Dwedit
Posts: 4351
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Mon Sep 26, 2011 9:56 am

Nestopia uses a persistent 1-byte buffer for 2007 reads.
You read from the PPU the very first time, you get garbage. (defined as 0xE8)
You read from the PPU again (no matter how much later you are doing this, 5 minutes later or an hour later, doesn't matter), you get the last value read from the PPU, and it puts the newly read byte into the buffer.
Only PPU reads through 2007 affect the 1-byte buffer.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

Bisqwit
Posts: 248
Joined: Fri Oct 14, 2011 1:09 am

oddly green

Post by Bisqwit » Fri Oct 14, 2011 2:38 am

In the same spirit I also ask for clues.

Here is a screenshot from my WIP emulator. Ignore the problem with the messed-up sprite or with the wrong background position. I am at a loss as to what causes part of the text to appear green. It is not even the first time this happens. (I mean, I have ran into the same problem with two previous emulators I have started developing from scratch within 10 years.)

Image

My first intuition would be to check the palette mirroring, but according to my best understanding I have implemented all the palette index mirrorings according to specifications...
That is, all reads/writes to palette indexes, whether internally or through I/O, are routed through the following map:

Code: Select all

 00 => 0   08 => 0     10 => 0    18 => 0
 01 => 1   09 => 9     11 => 11   19 => 19
 02 => 2   0A => A     12 => 12   1A => 1A
 03 => 3   0B => B     13 => 13   1B => 1B
 04 => 0   0C => 0     14 => 0    1C => 0
 05 => 5   0D => D     15 => 15   1D => 1D
 06 => 6   0E => E     16 => 16   1E => 1E
 07 => 7   0F => F     17 => 17   1F => 1F

Post Reply