When playing Zelda 1, my emulator is able to do horizontal mirroring without issue. However, when trying to move to east/west the screen transition is full of zeros: https://streamable.com/njl14z
At first I thought this might be an MMC1 dynamic mirroring bug, but after debugging my emulator is switching to vertical mirroring mode when moving east or west. This might be a long shot, but I'm wondering if anyone here has encountered a similar issue. The thread viewtopic.php?f=3&t=23260 discusses a similar issue, but no resolution was ever posted. Any suggestions?
Zelda 1 Vertical Mirroring Bug
Moderator: Moderators
Re: Zelda 1 Vertical Mirroring Bug
When I was trying to diagnose the problem in that other thread, I remarked that emulating 4-screen VRAM (i.e. four distinct nametables) produced the exact same effect you are observing - are you absolutely certain that your PPU code is actually obeying your mirroring settings?siliconandsolder wrote: ↑Mon Jul 05, 2021 7:41 pm When playing Zelda 1, my emulator is able to do horizontal mirroring without issue. However, when trying to move to east/west the screen transition is full of zeros
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
-
- Posts: 9
- Joined: Tue Apr 20, 2021 1:25 pm
Re: Zelda 1 Vertical Mirroring Bug
As far as I can tell, yes. I stepped through the emulator from MMC1 selecting the mirroring modeQuietust wrote: ↑Mon Jul 05, 2021 8:08 pmWhen I was trying to diagnose the problem in that other thread, I remarked that emulating 4-screen VRAM (i.e. four distinct nametables) produced the exact same effect you are observing - are you absolutely certain that your PPU code is actually obeying your mirroring settings?siliconandsolder wrote: ↑Mon Jul 05, 2021 7:41 pm When playing Zelda 1, my emulator is able to do horizontal mirroring without issue. However, when trying to move to east/west the screen transition is full of zeros
Code: Select all
match regVal & 3 {
0 => {
self.mirrorMode = MIRROR::ONESCREEN_LO;
}
1 => {
self.mirrorMode = MIRROR::ONESCREEN_HI;
}
2 => {
self.mirrorMode = MIRROR::VERTICAL;
}
3 => {
self.mirrorMode = MIRROR::HORIZONTAL;
}
_ => { panic!("Should never reach this."); }
}
Code: Select all
let realAddr = addr & 0x0FFF;
match self.cart.borrow().getMirrorType() {
MIRROR::HORIZONTAL => {
return match realAddr {
a if a < 0x0800 => { self.tblName[(a & 0x03FF) as usize].clone() }
_ => { self.tblName[(realAddr & 0x0BFF) as usize].clone() }
};
}
MIRROR::VERTICAL => {
return self.tblName[(realAddr & 0x0FFF) as usize].clone();
}
_ => { panic!("Unrecognized Mirror Type: {:?}", self.cart.borrow().getMirrorType()); }
}
Re: Zelda 1 Vertical Mirroring Bug
Your logic is wrong for both mirroring types - horizontal is effectively using nametables 0 and 2 (rather than 0 and 1, which is very important when mirroring can be dynamically switched), and vertical is allowing access to all 4 nametables at once (i.e. it's doing 4-screen VRAM, exactly as I mentioned above).siliconandsolder wrote: ↑Mon Jul 05, 2021 8:28 pm to the PPU reading the next tile addressI haven't even implemented 4-screen mirroring yet; the emulator would panic if MMC1 selected 4-screen mirroring.Code: Select all
let realAddr = addr & 0x0FFF; match self.cart.borrow().getMirrorType() { MIRROR::HORIZONTAL => { return match realAddr { a if a < 0x0800 => { self.tblName[(a & 0x03FF) as usize].clone() } _ => { self.tblName[(realAddr & 0x0BFF) as usize].clone() } }; } MIRROR::VERTICAL => { return self.tblName[(realAddr & 0x0FFF) as usize].clone(); } _ => { panic!("Unrecognized Mirror Type: {:?}", self.cart.borrow().getMirrorType()); } }
Horizontal mirroring should look like this:
Code: Select all
a if a < 0x0800 => { self.tblName[(a & 0x03FF) as usize].clone() }
_ => { self.tblName[((realAddr & 0x03FF) | 0x0400) as usize].clone() }
Code: Select all
return self.tblName[(realAddr & 0x07FF) as usize].clone();
Code: Select all
MIRROR::ONESCREEN_LO => {
return self.tblName[(realAddr & 0x03FF) as usize].clone();
}
MIRROR::ONESCREEN_HI => {
return self.tblName[((realAddr & 0x03FF) | 0x400) as usize].clone();
}
Also, while this setup will work for most games, I should warn you that it will not be sufficient for emulating some of the more advanced mappers where each nametable slot can be individually configured by the mapper, so at some point you're going to have to change it to accept raw bank numbers for each nametable (and potentially even custom data such as CHR ROM or the MMC5's internal EXRAM).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.