I've attached a Quicktime video of the crash I'm experiencing when trying to run Super Mario Brothers 1. During either the title demo or actual execution of the game, my emulator crashes when the screen starts showing the area just beyond the first pipe. Looking in a debugger on Nintaco this appears to be about the end of the data initially loaded into the second nametable. Has anyone seen anything like this before? My initial impression was that this was being caused by nametable mirroring being setup incorrectly. My code is below for finding the address when vertical mirroring. I have an array of 2048 bytes that I use for storing the 2 nametables.:
Code:
address = (address - 0x2000) % 0x1000; // 3000-3EFF is a mirror.
if (rom->verticalMirroring) {
address = address % 0x0800;
} else { // horizontal mirroring
if (address >= 0x400 && address < 0xC00) {
address = address - 0x400;
} else if (address >= 0xC00) {
address = address - 0x800;
}
}
return nametables[address];
The rest of my code is here:
https://github.com/davecom/DDNES/blob/m ... es/DDPPU.cThanks in advance.
The code snipped you posted here looks OK, though you may want to do some research about bitwise operators, which could help simplify this code for you.
...but are you saying your emulator application crashes, or that the emulated SMB game itself crashes? If your application crashes, you should build and run it in debug mode and see what information your debugger can give about the crash. If the emulated game is crashing... I think in most cases mapping the wrong nametables should just be a visual problem and not hang the game, so it could be caused by bad nametable logic but seems more likely that the problem is elsewhere.
I think I remember reading that SMB1 will crash if its sprite 0 hit doesn't happen?
Sorry, I should have been more clear. The game crashes, not my emulator. When I look at what's going on in my emulator, it just gets into the usual checking for sprite zero hit loop and continues to execute. As you can see in the video, the other thing that happens is the sceen takes on a green tint when this crash occurs, which seems to me that the nametables are being written over incorrectly somehow, which is another reason I suspected the mirroring (and also since it seems to happen exactly at the end of the second nametable).
The reason I didn't think it was sprite zero is that I am passing the basic sprite zero hit test ROM and SMB was not even getting past the title screen until I had sprite zero hit working.
Sure? The sprite 0 hit needs to happen every vsync, and if something keeps the right pixels from being in the right place on screen, the poll loop will hang at that point.
edit: let me restate that. I'm guessing the sprite 0 isn't the cause of the bug, but is instead the symptom that leads to a crash instead of some other flaw.
In your video, once the status bar disappears, the sprite-0 hit has no background to trigger it. So that does seem like a probable cause for a hang given what lidnariq said.
...but the thing to determine here is why the status bar disappeared. I don't think it's in the nametable mirroring logic you posted. (Really the only line that should be running from that snippet is address = address % 0x0800; which seems trivially correct.)
lidnariq wrote:
edit: let me restate that. I'm guessing the sprite 0 isn't the cause of the bug, but is instead the symptom that leads to a crash instead of some other flaw.
Yeah that's what I've been thinking too. That the sprite 0 isn't the cause of the bug, but the symptom of something wrong in the name-tables.
Perhaps I don't fully understand how SMB utilizes the two nametables. One thing I notice looking at the debugger in Nintaco is that the status bar is always coming from the first nametable. So the fact that it disappears, says to me that maybe my first nametable is getting overwritten after I reach the end of the second nametable. In other words, my guess is that once the first nametable start to be written at $2800, is when I am having the problem. This is why I originally suspected mirroring. However, since it fills this in one vertical set of tiles at a time, I would think just the beginning of the edge of the screen would become corrupted?
Interesting possibly unrelated detail. I pass all of blarg's vram_access tests, but I am failing #3 of his sprite_ram tests. Notably "Address should increment on $2004 write." I doubt this is related, but I will look into it.
Edit: Yeah it was unrelated. I fixed that and now pass all of blarg's sprite_ram tests but still the bug is exactly the same.
I think very few games access OAM directly through $2004. It's not normally very useful to do so, so most exclusively use the DMA through $4014.
If I had to take a stab in the dark, I'd guess you've either got a(nother) CPU bug and it's calculating the wrong address, or your autoincrement behavior is going wrong on the PPU.
The scrolling result, and the disappearance of the status bar, is expected after it fails a sprite 0 hit. The NMI never fires, so it never reverts the horizontal scroll for the status bar.
Thanks for helping me think through it. Yeah quite confident it's not a CPU bug—have passed all the CPU tests: blarg, nestest, nestress, etc. I've run quite a few and a few games run perfectly (Donkey Kong, Tennis, Baseball), so I don't think it's that.
I do think you might be right about an increment somewhere in the PPU, especially since I missed the sprite increment one in 2004. I also find what you wrote interesting about sprite zero. But isn't the little sprite zero in that part of SMB always in the same place? It's always by that coin right? So, why would that be suddenly a problem after working earlier in the level unless the coin stops showing up because the nametables are messed up?
The coin is a nametable tile. The sprite 0 is a little wedge placed over the bottom 2 pixels of that coin.
If the status area of the nametable isn't displayed in the right position (scroll register problem, maybe?) or if that byte of the nametable gets overwritten with something blank, the sprite has nothing to test against.
(I can't tell from the video whether the scroll is wrong, or the nametable has been overwritten.)
I did a lot of investigating tonight. I'm pretty sure at this point that it's the nametables getting overwritten. Now, I need to figure out where they're getting overwritten from. I see the last action of my emulator before the game gets stuck is writing a column of tiles. Cleary these tiles are being written from the wrong bit of memory. Anyway, I'll keep looking. I'm pretty sure this is 1 line of code somewhere, but I may end up needing to write a debugger that shows the nametables as the game progresses so I can see the mistake happen live.
My emulator doesn't support $2003 or $2004 at all and SMB runs fine.
If the game just hangs like that it's unlikely to be a PPU issue since this game never waits for PPU to do its job except for sprite 0.
Right, name tables get written by $2007. I can see incorrect tiles being written there right before the crash.
Okay I finished building my nametables debugger. You can find a "just before the crash" screenshot of both the game (small window) and the 4 nametables (big window) here:
Attachment:
File comment: Before the crash screenshot.
before.png [ 132.84 KiB | Viewed 3440 times ]
Then you will find an "after the crash" screenshot here:
Attachment:
File comment: After the crash screenshot.
after.png [ 142.69 KiB | Viewed 3440 times ]
As you can see from the screenshots, the text and coin on the top actually appears to be okay in the nametables! The mystery deepens.
EDIT: Originally was failing a timing test ROM. I have since fixed that—I wasn't counting the +1 ticks for successful branches. Fixed. SMB still crashing.
Okay I solved it!
As I suspected it did have to do with pulling from the wrong nametable address and it also was a one liner!
For nametable byte address fetch I was using:
Code:
address = (0x2000 + NAME_TABLE_ADDRESS) | (V & 0x0FFF);
Where NAME_TABLE_ADDRESS was
Code:
((((word)PPU_CONTROL1) & 0b00000011) * 0x400)
But I noticed in Fogleman's emulator that actually I just needed the simpler:
Code:
address = 0x2000 | (V & 0x0FFF);
I guess V already encodes the specific nametable... okay SMB is working now. Thanks everyone.