I made a full-screen text renderer that uses the FME-7 mapper to switch CHR banks. I need FME-7 because it's one of the few mappers supporting more than 8K of SRAM at $6000. The text looks fine in FCEUX and on my PowerPak. But it's a smear of flickering crap in Mednafen, and I'm told it looks similar in Nestopia. I can't debug why because Nestopia has no debugger and I can't seem to understand the manual for Mednafen's. Nor can I tell whether the problem is in the emulators or in my program because I lack an FME-7 devcart. So what should I try next?
I start the first timer period at the start of the NMI handler. Because the FME-7's timer has a fixed reload value of $FFFF, instead of a configurable period value like the MMC3's timer, I have to make later periods a multiple of 256 CPU cycles so that I can write only the high period byte.
Is anyone willing to test this on a modded FME-7 cart? No licensed FME-7 game includes CHR RAM, but if you want, I can make an equivalent demo that uses CHR ROM.
EDIT: In #nesdev, Myask told me that Mednafen's debugger doesn't display properly below 3x scale. My laptop's display can handle only up to 2x.
The only obvious difference to me between FCEUX's and Nestopia's IRQ implementation is that FCEUX acknowledges the interrupt[1] when code writes any value to the three IRQ registes, but Nestopia's doesn't[2].
[1]:
Code:
case 0xD: IRQa = V; X6502_IRQEnd(FCEU_IQEXT); break;
case 0xE: IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break;
case 0xF: IRQCount &= 0x00FF; IRQCount |= V << 8; X6502_IRQEnd(FCEU_IQEXT); break;
[2]:
Code:
case 0xD:
irq.Update();
irq.unit.enabled = data & 0x01;
if (!irq.Connect( data & 0x80 ))
irq.ClearIRQ();
break;
case 0xE:
irq.Update();
irq.unit.count = (irq.unit.count & 0xFF00) | data << 0;
break;
case 0xF:
irq.Update();
irq.unit.count = (irq.unit.count & 0x00FF) | data << 8;
break;
Thanks for finding the relevant source code.
I've been acknowledging IRQ by writing $80 then $81 to reg $0D. Based on my understanding of
the wiki, bit 7 is "count" and bit 0 is "generate IRQs", and turning IRQ generation off and back on will acknowledge IRQ. It looks like in Nestopia, you have to stop the counting (write $00 then $81). That'd effectively extend each IRQ period by 6 cycles, making the usable periods 256*n + 6 cycles. I've added a demo that uses this 256*n + 6 technique. The new demo works in Mednafen and Nestopia. Both work on the PowerPak.
Anyone want to help try a few tests for me on Sunsoft silicon?
@tepples
Did you use unofficial opcodes? An unofficial opcode $CB was trapped at PC=$E4A9!
Plus, it uses CHR RAM but still writes for CHR ROM swaps. Why?
Zepper wrote:
Did you use unofficial opcodes? An unofficial opcode $CB was trapped at PC=$E4A9!
Yes.
AXS #ii is the only unofficial opcode that I regularly use at the moment, and even then only in specific to the NES PPU.
Quote:
Plus, it uses CHR RAM but still writes for CHR ROM swaps. Why?
It swaps banks of CHR RAM to allow use of more than 256 distinct tiles on the screen.
tepples wrote:
Zepper wrote:
Did you use unofficial opcodes? An unofficial opcode $CB was trapped at PC=$E4A9!
Yes.
AXS #ii is the only unofficial opcode that I regularly use at the moment, and even then only in specific to the NES PPU.
Why are you doing this? Did you just not witness a reason not to?
To save 16 cycles during vblank.
To eliminate unofficial opcodes as a factor in the test, I have added a second build-time option called AVOID_UNOFFICIAL_OPCODES, in addition to the existing build-time option called NESTOPIA_WORKAROUND, and built two more ROMs without unofficial opcodes, one with and one without the Nestopia workarounds. The ROMs built without unofficial opcodes behave (or misbehave) the same way in Mednafen as the corresponding ROMs with unofficial opcodes.
koitsu wrote:
Why are you doing this? Did you just not witness a reason not to?
What reason? Emulator inaccuracies?
Reason is in the quoted material, and that isn't "emulator inaccuracy".
Bottom line: if you want to be as compatible as possible (I am not referring to just emulators -- what makes you think NES-on-a-chip implements said opcodes, or any other non-Nintendo-sanctioned device?) then don't use undocumented opcodes. (And I will not tolerate splitting hairs over the use of the word "undocumented" vs. "unofficial" -- you know what I mean).
Same general advice applies to some PPU features as well (e.g. emphasis bits).
I classify use of undocument opcodes the equivalent of "hipster focus". Stop trying to show off/screw around and just write shit that you know is going to work universally. Come on -- deep down inside you know what you're doing is silly.
koitsu wrote:
I classify use of undocument opcodes the equivalent of "hipster focus". Stop trying to show off/screw around and just write shit that you know is going to work universally. Come on -- deep down inside you know what you're doing is silly.
I would say that using these opcodes is justifiable if they allow you to do something concrete that wouldn't be possible without them. On the NES that would be almost never in an actual game, but a realtime 3D demo for example could get a decent frame rate boost by making any part of the rendering pipeline faster.
On the Atari 2600, where you only have a measly 76 cycles per scanline and a primitive video system with memory for less then 1 scanline, unofficial opcodes could mean more colorful/detailed players and backgrounds. In that case, I consider unofficial opcodes more than welcome, otherwise I agree that it just looks like people are showing off.
My point is that I find it OK to risk sacrificing compatibility if the alternative is sacrificing an interesting feature. At the same time, I think that if you're copying an NES, your product should be able to do what an NES does if you want to call it accurate. If you want to skip implementing such obscure details, like many people do when few games rely on such details, that's fine, each project has its scope and limitations, but there's no denying that this is an inaccuracy.
Reasons to use illegal opcodes:
1. It's "fun".
2. It breaks on low quality emulators, encouraging them to improve.
3. It saves cycles.
Reasons to avoid illegal opcodes:
4. It breaks on low quality emulators, discouraging people from using your software.
If you don't care much who uses your software, reason 4 could be irrelevant, leaving you with no reason not to use them. I think reason 3 is usually irrelevant as well; the cycle savings aren't normally meaningful, though they could be in the right situation. I suspect Tepples is motivated by 1, finding it fun to try out an obscure feature, and by 2, and if you don't have a financial stake in your software it's not as much a problem if you don't care about 4. You have to care about the popularity of your software for 4 to be important.
For a test like this which is explicitly about improving emulation knowledge (the whole point is finding incompatibilities and learning from them), it doesn't seem to me that reason 4 is relevant at all.
Here's another test specifically for the acknowledgment behaviors I'm seeing. No unofficial opcodes.
The demo tries to acknowledge the interrupt in one of six ways:
- Writing value of $00, $01, $80, or $81 to register $0D
- Writing value of $FF to register $0E or $0F
These are the results I get:
- FCEUX: all Acked
- Mednafen: 0D=00 and 0D=01 Acked; others No ack
- PowerPak: 0D=00 and 0D=80 Acked; others No ack
EDIT: I mentioned it on #nesdev.
- tpw_rules tried it on Everdrive and got the same result as PowerPak
tepples wrote:
Here's another test specifically for the acknowledgment behaviors I'm seeing. No unofficial opcodes.
The demo tries to acknowledge the interrupt in one of six ways:
- Writing value of $00, $01, $80, or $81 to register $0D
- Writing value of $FF to register $0E or $0F
These are the results I get:
- FCEUX: all Acked
- Mednafen: 0D=00 and 0D=01 Acked; others No ack
- PowerPak: 0D=00 and 0D=80 Acked; others No ack
EDIT: I mentioned it on #nesdev.
- tpw_rules tried it on Everdrive and got the same result as PowerPak
clarification for posterity: Mednafen's debugging console is 640x480, (at least in NES mode, haven't tried others). x2 scaling for NES on default settings (top/bottom 8 scanlines hidden) is 512x448.
Thus, (2.5, 2) scaling minimum with all scanlines displayed.
Thanks for the confirmation, l_oliveira.
In #nesdev, Quietust confirmed that he'll be implementing the newly confirmed behavior in coming Nintendulator beta versions. Now we just need to hit up the bug trackers for FCEUX and other major emulators.
I've now read the entire thread twice and I honestly can't figure out what the "correct" behaviour should be. Could we get a short/terse write-up that concisely explains what the proper behaviour should be? That should greatly help with submitted bug reports to emulator authors.
Correct behavior: All writes to register "D" (i.e. writing to $Axxx after $0D has been written to $8xxx) should acknowledge the IRQ, regardless of the value written. Writes to registers "E" and "F" shouldn't.
Watch this space for progress of fixing FME-7 acknowledgment:
- Nintendulator: In #nesdev, Quietust reports it fixed
- FCEUX: I filed a bug report, and rainwarrior committed a fix (diff; Windows interim build).
- Mednafen: I needed help with the registration form's CAPTCHA (and on identifying the mascot) on #nesdev, and then it took a while to receive registration confirmation from forum.fobby.net and a while after that for a moderator to approve the bug report.
- Nestopia UE: GitHub issue report has been closed with a fix.
- NO$NES: Haven't had time to download, install, and test, but the description of FME-7 in Everynes appears incomplete
- RockNES, BizHawk: Reported fixed below
- EverDrive: In #nesdev, tpw_rules said "krizz told me he'd fix it for everdrive"
- PowerPak: In #nesdev, loopy reports that he has updated the FME-7 implementation in the mapper pack I use.
Tested Nestopia UE patch:
Code:
diff --git a/source/core/board/NstBoardSunsoftFme7.cpp b/source/core/board/NstBoardSunsoftFme7.cpp
index f3ee5d2..535456f 100644
--- a/source/core/board/NstBoardSunsoftFme7.cpp
+++ b/source/core/board/NstBoardSunsoftFme7.cpp
@@ -170,9 +170,8 @@ namespace Nes
irq.Update();
irq.unit.enabled = data & 0x01;
-
- if (!irq.Connect( data & 0x80 ))
- irq.ClearIRQ();
+ irq.Connect( data & 0x80 );
+ irq.ClearIRQ();
break;
Nestopia UE -- filed
issue 124
You can provide the copyrighted FME-7 cassette transformed into flash cassette , The test results on FC/NES?
RockNES seems OK.
tepples wrote:
To save 16 cycles during vblank.
To eliminate unofficial opcodes as a factor in the test, I have added a second build-time option called AVOID_UNOFFICIAL_OPCODES, in addition to the existing build-time option called NESTOPIA_WORKAROUND, and built two more ROMs without unofficial opcodes, one with and one without the Nestopia workarounds. The ROMs built without unofficial opcodes behave (or misbehave) the same way in Mednafen as the corresponding ROMs with unofficial opcodes.
Works fine in RockNES now. The Nestopia version flashes the bottom of the image (around 40 or more lines).
fixed in bizhawk's neshawk and quicknes cores in r9193
Nestopia UE
issue 124 has been committed/fixed.
Binaries are usually made available over on
EmuCR except it seems the last build they've offered is
from 2014/10/07. I don't know what changed to make them stop doing builds, but I'll see if I can find out.
Edit: seems the builds on EmuCR aren't of master -- they're only when a new tag (I think) is made; the 2014/10/07 release correlates with the last/latest git tag 1.46.2. I'll see about asking if they can add one for master.
Does this still need testing on a real cart? I couldn't tell from the posts if someone actually did that.
l_oliveira ran it on a cart and got this result. The task has become to get emulators to give the same result.