I was working on learning mid-frame scrolling (test rom was a Top Status Bar with an IRQ to scroll the bottom). I read the documentation and got it to work but Nestopia and FCEUX had a conflict with each other on the $2005 writes. Since Nestopia is generally more accurate and the same as the wiki I figure it is correctly written. Is there anything that could be possibly missing code-wise that could cause FCEUX to have the $2005 writes switched or is it just an emulator issue? I tried reading and not reading $2002 before and placing the code in Forced Blank and not but nothing seemed to make the two emulators match.
; NN = NAMETABLE
; YYYYY = SCROLL TILE DOWN
; XXXXX = SCROLL TILE RIGHT
; yyy = SCROLL PIXEL DOWN
; xxx = SCROLL PIXEL RIGHT
Working in Nestopia 1.39:
Code:
LDA XSCROLL+1 ; %----NN--
AND #%00000001
ASL
ASL
STA $2006
LDA YSCROLL+0 ; %YY---yyy
STA $2005
LDA XSCROLL+0 ; %-----xxx
STA $2005
LDA YSCROLL+0 ; %YYY-----
AND #%00111000
ASL
ASL
STA TEMP_SCROLL
LDA XSCROLL+0 ; %---XXXXX
LSR
LSR
LSR
ORA TEMP_SCROLL
STA $2006
Working in FCEUX 2.2.1:
Code:
LDA XSCROLL+1 ; %----NN--
AND #%00000001
ASL
ASL
STA $2006
LDA XSCROLL+0 ; %-----xxx
STA $2005
LDA YSCROLL+0 ; %YY---yyy
STA $2005
LDA YSCROLL+0 ; %YYY-----
AND #%00111000
ASL
ASL
STA TEMP_SCROLL
LDA XSCROLL+0 ; %---XXXXX
LSR
LSR
LSR
ORA TEMP_SCROLL
STA $2006
What works on the NES is
tokumaru's code fragment.
The timing is as important as the operations you are performing. If you do some things too soon the PPU will screw up your scroll values, too late and you'll get a glitchy scanline. If you look at my code, I wrote some notes about the timing. You can do a lot of stuff ahead of time, at any point before the HBlank, and only the last couple of writes have to fall within HBlank. That's just 8 cycles in a a window of ~28 cycles, so it isn't so hard to get right.
tokumaru wrote:
only the last couple of writes have to fall within HBlank. That's just 8 cycles in a a window of ~28 cycles, so it isn't so hard to get right.
Better yet, it's actually 5 cycles: the write from
stx $2005, three cycles to fetch
sta $2006, and the write from
sta $2006.
Yeah, I thought about considering only the last cycle of the $2005 write, but figured that 5 cycles would sound confusing. =)
What I'm not really sure is the ~28 figure though, because at the very start of HBlank the VRAM pointer is automatically updated, and by the end the tiles for the next scanline are already being fetched, so you definitely want the new scroll values to be set by them. My advice is: try to make those timing sensitive writes closer to the beginning of HBlank, right after the VRAM pointer auto updates.
Thanks, I tried the code tokumaru posted and ended up with the same exact thing happening. I understand what HBlank is but maybe I'm not correctly figuring out when it occurs. The test rom below scrolls smoothly vertically on Nestopia and Nintendulator with no glitching but is still not right in FCEUX as its trying to do the $2005 writes in a backwards order (so I'm hoping its the emulator).
Test Rom
FCEUX sucks with the scroll timings, go off of Nestopia for stuff like that.
Does your code work on the actual NES/Famicom? If so, then you're done/no further effort is required. Writing code that "caters to emulator oddities" is the wrong way to go about it.
koitsu wrote:
Does your code work on the actual NES/Famicom? If so, then you're done/no further effort is required. Writing code that "caters to emulator oddities" is the wrong way to go about it.
I have no idea. I have no way to test on the real machine.
I'll say this again: IF A PIECE OF CODE WORKS ONLY ON HARDWARE, MOST OF THE TIME IT HAS NO EXCUSE FOR FAILING ON EMULATORS. PLEASE DON'T GO "IT WORKS ON HARDWARE, SO FUCK FCEUX/NINTENDULATOR/NESTOPIA/ETC", BECAUSE YOU COULD BE RELYING ON SOME EDGE CASE THAT COULD CAUSE PROBLEMS UNDER CERTAIN CONDITIONS.
Unless you are doing something unusual (I wouldn't put mid-screen scroll changes in that category - in fact, FCEUX in particular is pretty forgiving in this area), you should take a look into it if your code is failing on popular emulators.
On the other hand, it leads to stall in emu dev scene. Like, if there are no games that shows certain bugs in emulators, the bugs will never get fixed. I'm personally pretty sure that if some code works on the HW, it shouldn't be fixed for emulators, the emulators should be fixed instead.
That may be not very actual on the NES scene, as the system is relatively simple, and emulators are well developed, but on the SNES scene it could be very difficult to satisfy all popular emulators. Happens very often, a game works properly in one, shows glitches in another, when you fix it to work in both, it starts to glitch on the third, and that's a vicious circle.
koitsu, I have to disagree with you. When you are experimenting undocumented behavior, like manipulating registers at odd times, using open bus values, etc. then yes, what the hardware does should be the definitive answer (even though different revisions might behave differently). But when you're working with behavior that has been documented for years (loopy's skinny doc), and has been implemented in every NES emulator made in the past 10+ years, a failure on emulators could mean that something is wrong, even if it appears to work 100% on hardware.
I wouldn't call that "catering to emulator oddities", because these emulators were made to work with this kind of code. They were supposed to handle this correctly, and if they aren't, that might be a sign that your code isn't as safe as it could be. Just because you didn't see any errors during the 1 minute you tested your code on your own console, that doesn't mean that inside a game engine running for several hours on a console on the other side of the planet the same code wouldn't cause problems because it wasn't safe enough.
tokumaru wrote:
koitsu, I have to disagree with you. When you are experimenting undocumented behavior, like manipulating registers at odd times, using open bus values, etc. then yes, what the hardware does should be the definitive answer (even though different revisions might behave differently). But when you're working with behavior that has been documented for years (loopy's skinny doc), and has been implemented in every NES emulator made in the past 10+ years, a failure on emulators could mean that something is wrong, even if it appears to work 100% on hardware.
I wouldn't call that "catering to emulator oddities", because these emulators were made to work with this kind of code. They were supposed to handle this correctly, and if they aren't, that might be a sign that your code isn't as safe as it could be. Just because you didn't see any errors during the 1 minute you tested your code on your own console, that doesn't mean that inside a game engine running for several hours on a console on the other side of the planet the same code wouldn't cause problems because it wasn't safe enough.
None of this has any relevancy to
this specific issue at hand -- he's using your code (now/at this point), which we know works on actual hardware (right?), yet doesn't work on one emulator. Thus I do think it's safe to say in this case "fuck FCEUX". :-) I bet it doesn't work on Nesticle either -- oh shit, what do we do!? Maybe the guy should provide the full source code (test ROM = a good start, but the code matters more in this case).
Shiru wrote:
Happens very often, a game works properly in one, shows glitches in another, when you fix it to work in both, it starts to glitch on the third, and that's a vicious circle.
Like I said, you have to consider the kind of work you're doing. Reading the controller, for example. It's a very basic thing that every emulator must be able to do in order to play games, so if input is broken in your game alone you must be doing something funky. Unless you have a very specific reason for doing it in a funky way, I think you should change it. I see no point in breaking things on purpose.
For complex tasks however, sure, you shouldn't give up features or effects in your game just because emulators can't handle them.
koitsu wrote:
he's using your code (now/at this point), which we know works on actual hardware (right?), yet doesn't work on one emulator.
It works correctly when run at the correct time. I'm pretty sure I tested it in FCEUX... (will try again when I get the chance, just to be sure) Maybe he's not getting the timing right.
Quote:
I bet it doesn't work on Nesticle either -- oh shit, what do we do!?
That was very constructive! Nesticle was last updated well over 10 years ago though, so my point still stands.
Quote:
Maybe the guy should provide the full source code (test ROM = a good start, but the code matters more in this case).
A test ROM would be great for us to check the timing out.
I had the link to the rom above and here is a direct attachment to it again. I can provide the source if needed but its just a rom for testing that I keep adding features to make sure I can do various things myself.
Attachment:
skeleton.nes [24.02 KiB]
Downloaded 102 times
Indeed this works in Nestopia and Nintendulator, which says that the timing is correct. If you add/remove NOPs can you make it look right in FCEUX? I wonder if the MMC3 IRQ timing isn't wrong in that emulator.
Now you guys are making me doubt it ever worked in FCEUX!!! Seriously though, I remember it working there, but I think I used a sprite 0 hit to time the effect, not an IRQ. I could be wrong though.
I use a highly modified version of tokumarus post (I basically rewrote it, but it's very identical) and it works on Nestopia, and fails in FCEUX and Nintendulator, and it also works on a real system.
I'd say that working in Nestopia, Nintendulator and real hardware but failing in FCEUX is better than any other permutation of those elements! =) I will have to see what's going on in FCEUX though... it's definitely possible to get a clean split on it, I've done it before.
Again -- if he would just post the full source code...
When I said "a test ROM is a good start", I meant "he's already provided a test ROM in his earlier post, but disassembling that and reverse engineering it is stupid when the full source can just be disclosed". And by full I do mean full -- because, to me, it sounds like there are some PPU adjustments that are being done somewhere "before tokumaru's code" causing the anomaly, thus tickling some nonsense in FCEUX. Without the full code, folks helping are effectively forced into jacking off with chopsticks (i.e. wasting time).
koitsu wrote:
Again -- if he would just post the full source code...
Here is the full code. Its kind of cluttered as it wasn't really meant to be viewed by others.
Attachment:
skeleton.txt [44.35 KiB]
Downloaded 83 times
Some preliminary debugging has led me to believe that this is a problem with the timing of the MMC3 IRQ. Look at the pattern tables and you'll see that sprites tiles are on the left and background tiles on the right, the opposite of what's normal in MMC3 games. The scanline counter in the MMC3 is very picky, and if you break its rules it can fire at different times or behave erratically. My guess is that FCEUX does not emulate the alternate counting that results from inverting sprites and backgrounds, and fires at the usual time. This screws up the timed code that follows, which is supposed to make the last 2 PPU writes happen at the start of HBlank. I'm not 100% sure this is the problem, but it's my best guess so far.
EDIT:
Here's some reference about about MMC3 IRQ timing:
NESDEV Wiki wrote:
If the BG uses $0000, and the sprites use $1000, then the IRQ will occur after PPU cycle 260 (as in, a little after the visible part of the target scanline has ended).
If the BG uses $1000, and the sprites use $0000, then the IRQ will occur after PPU cycle 324 of the previous scanline (as in, right before the target scanline is about to be drawn).
tokumaru wrote:
Some preliminary debugging has led me to believe that this is a problem with the timing of the MMC3 IRQ. Look at the pattern tables and you'll see that sprites tiles are on the left and background tiles on the right, the opposite of what's normal in MMC3 games. The scanline counter in the MMC3 is very picky, and if you break its rules it can fire at different times or behave erratically. My guess is that FCEUX does not emulate the alternate counting that results from inverting sprites and backgrounds, and fires at the usual time. This screws up the timed code that follows, which is supposed to make the last 2 PPU writes happen at the start of HBlank. I'm not 100% sure this is the problem, but it's my best guess so far.
I thought of that earlier and tried flipping the Tile Patterns in the $2000 register but it was producing the same effect. FCEUX remained broken with the same movement and Nestopia worked but had a graphical glitch at the end of the split (as expected from the timing being changed).
I debugged some more and it appears that the IRQ fires too soon in FCEUX. By the time the your IRQ handler starts (i.e. the code at $C38C), FCEUX is around PPU cycle/pixel 306, while Nintendulator is around cycle 11 of the next scanline. Quite a big difference, enough to have your PPU writes that should take place during HBlank happening too soon.
Since, according to the wiki, the correct behavior would be for the NMI to fire at cycle 324, FCEUX is obviously in a hurry. Nintendulator on the other hand, appears to be correct, considering that the IRQ can happen in the middle of an instruction (which has to finish before the IRQ can be serviced) and that the CPU needs some time (7 cycles?) to actually enter the IRQ routine, cycle 11 of the next scanline sounds about right (3 cycles of a possible unfinished instruction + 7 cycles to enter the IRQ handler = 10 CPU cycles = 30 PPU cycles; 324 + 30 = 354; since each scanline is 341 PPU cycles long, 354 - 341 = 13).
So now that you know for sure that the problem is in FCEUX, you have to make a decision: will you try to fix the problem? If yes, I'd suggest you switch the sides of the sprite and background tiles, use the standard MMC3 configuration and adjust the timing of the PPu writes according to that configuration, to see if FCEUX agrees with the other emulators in that case.
You can obviously chose to ignore the problem, because you know it's nothing wrong with your software. But if you ask me, it's important to know WHY something is not behaving as expected, to make sure the problem is not in your work.
tokumaru wrote:
So now that you know for sure that the problem is in FCEUX, you have to make a decision: will you try to fix the problem? If yes, I'd suggest you switch the sides of the sprite and background tiles, use the standard MMC3 configuration and adjust the timing of the PPu writes according to that configuration, to see if FCEUX agrees with the other emulators in that case.
You can obviously chose to ignore the problem, because you know it's nothing wrong with your software. But if you ask me, it's important to know WHY something is not behaving as expected, to make sure the problem is not in your work.
Mostly I wanted to see what would happen. I flipped the CHR Bank data, switched the $2000 Tile Patterns bits, and added NOPs to the new HBlank. FCEUX remains with the same exact action but the glitch at the split is now gone from that emulator and Nestopia/Nintendulator still work properly.
I don't see anything strange in my code but maybe someone else will spot something.
jstout wrote:
FCEUX remains with the same exact action
You mean the weird jumping around of the background? I found that pretty weird, but it only appears to happen in version 2.2.1, in version 2.2.0 there was only a small glitch at the end of the scanline where the split happens. I'm not sure what the hell is going on with FCEUX, sometimes it behaves very oddly.
I downloaded FCEUX 2.2.0 and the current interim build 2.2.2-2963 this morning and the rom worked correctly on both but not FCEUX 2.2.1. So it seems apparent it was a bug that was introduced in that emulator version and then fixed later on.