Page 1 of 3

Posted: Sun Mar 28, 2010 10:37 am
by tokumaru
In [url=http://nesdev.com/bbs/viewtopic.php?p=59150#59150]this post[/url], tepples wrote:If you're willing to make your game loop look like this:
I don't like to discuss the same old thing again, but I have to point out that there is an obvious disadvantage to this method: IT WILL MISS VBLANKS. Maybe not for Pong, Tetris or a card game, because those are likely to always finish all their frame calculations before VBlank. However, once the games get more complex and they are not guaranteed to finish the calculations in time, the NMI will fire but the game logic will soon be running again like nothing happened.

I know that it's possible to detect missed VBlanks because you use a counter, but that will not solve all problems. In my game for example I keep rendering disabled for some extra scanlines because I need the extra time for video updates and to hide vertical scrolling glitches. The thing is that even during lag frames I must blank those lines, even if I'm not writing anything to VRAM, otherwise the screen would jump.

And I need to know exactly when the NMI fires for timing reasons, otherwise I wouldn't be able to blank a constant number of lines, which also would cause the screen to jump.

I don't mean to be rude tepples, really, but I have to ask you this: Why do you keep teaching people your method (I know you are proud of it) when you know it's flawed under certain circumstances? It can be slightly friendlier to newbies who are used to polling $2002, but in the long run everybody wants to expand their games, so why teach them something that could break the games once they reach a certain point of complexity?

Hopefully the coders will be able to figure out the problem by themselves when their projects are more complex, but still. Besides being slightly friendlier to newbies, the only advantage I see it that it makes easier to have different VBlank routines for the different parts of the game, but that could easily be solved with a pointer in RAM (that can be changed whenever the programmer wants to) that the NMI jumps to as soon as it fires.
It doesn't modify anything but the flags, so it doesn't need to push anything else.
So does the one I posted in this very thread, and it doesn't choke on lag frames.

You seem to be very proud of your solution, but I guess I am of the one I'm using in my game as well. It's actually a hybrid of both methods, that allows you to wait for VBlank or use a custom NMI routine. Look at it, straight from my game:

Code: Select all

;-- INTERRUPT ---------------------------------------------------
;DESCRIPTION:
; This routine is responsible for indicating that VBlank started
; or for calling a custom NMI routine in case one is defined.
;----------------------------------------------------------------
NMI:

	;branch if the program was waiting for a regular interrupt
	bit WaitingVBlank
	bmi +FlipFlag

	;branch if there is no custom interrupt routine defined
	bit CustomNMI+1
	bpl +Return

	;transfer control to the the custom routine
	jmp (CustomNMI)

+FlipFlag:

	;indicate that the interrupt happened
	inc WaitingVBlank

+Return:

	;return from the interrupt
	rti
If the program was not waiting for VBlank and there isn't a custom NMI defined (I have one for each section of the game, except the simple ones that can work simply waiting for VBlank) the interrupt simply goes unnoticed, but if you wanted you could use the "counter approach" to detect missed frames.

Sorry if I seem "pissed off" or something in this post, it's not like that, and I don't have anything against you, it's just that whenever I see someone suggesting a solution that they know is not optimal I wonder why the hell they do that... Is it so the person that asked for help later comes back when the flawed solution breaks?

Posted: Sun Mar 28, 2010 10:41 am
by 3gengames
I understand what your saying but it's fine that way he is showing, and yes, the more complex they get it does matter alot, but I think the coder/programmer could figure it out by then :D



Re-programming, not working still but let me fiddle with it a bit more before I upload again..... :roll:

Posted: Sun Mar 28, 2010 11:05 am
by tokumaru
If you want an example of a situation where the "wait for VBlank" approach would fail, you don't even have to think about very complex games. Everyone would like to make a side scroller like SMB, with a status bar at the top.

OK, in order to have that status bar, you have to set the scroll every frame so that it's locked into position and doesn't scroll with the level. Then, a common way to detect the end of the status bar and the start of the playfield is a sprite 0 hit. Now, can you imagine how this could go wrong if you miss a VBlank?

First, you will not reset the scroll and will not lock the status bar into place, so it will show up either moved to the left or to the right. Second, the detection of the sprite hit will not be reliable anymore, either because the background is offset and is not guaranteed to hit the sprite anymore (meaning the game would freeze) or because you might start waiting for it after it has already happened (meaning you'd reposition the X scroll for the playfield at the wrong scanline).

This is why it's important to have a VBlank method that still allows you to do critical operations (like setting up a status bar) during lag frames, because there *are* operations that need to be performed even during lag frames, and if you plan on gradually increasing the complexity of your games there will eventually be lag frames.

Now that I think of it, I seem to remember someone saying that SMB does have issues with its status bar when too much action is going on. I personally haven't seen it, but I wouldn't be surprised if this was the case, since SMB does not do the main / VBlank thread separation I suggested.

Posted: Sun Mar 28, 2010 11:40 am
by tepples
What's a semma for?
tokumaru wrote:Why do you keep teaching people your method (I know you are proud of it) when you know it's flawed under certain circumstances?
I seem to remember it's good enough for Square. If your music is slowing down because you miss vblanks, your game is slowing down too, and you should either optimize your code or just do a Micronics and take the whole thing down to 30 fps. Otherwise, you'll have people complaining that they missed the jump because the frame rate changed.
it's just that whenever I see someone suggesting a solution that they know is not optimal I wonder why the hell they do that
No solution is optimal in all cases. For each solution, one must trade off the time to get the solution running, the risk of a failure, and the consequences of a failure. For newbies, I just think a Square-style solution with a direct counterpart to vsync() in Allegro is easiest to understand.
Is it so the person that asked for help later comes back when the flawed solution breaks?
Define a "break". A split-thread solution will "break" if you try to upload an inconsistent data set to the PPU, and I can think of situations where it would break hard if you don't know semaphores.
OK, in order to have that status bar, you have to set the scroll every frame so that it's locked into position and doesn't scroll with the level. Then, a common way to detect the end of the status bar and the start of the playfield is a sprite 0 hit. Now, can you imagine how this could go wrong if you miss a VBlank?
The status bar code from my unreleased side-scroller engine has this:

Code: Select all

@wait4endvbl:
  bit $2002
  bvs @wait4endvbl
@wait4s0:
  bit $2002
  bmi @s0giveup
  bvc @wait4s0
There are two loops here. The first waits for end of vertical blanking. The second waits for the sprite 0 hit on scanline 30 and prepares to set the X scroll position. The @s0giveup is a safety feature: if sprite 0 fails, it won't freeze like SMB1; instead, it will flicker a bit and continue.

And what happens if your status bar is at the bottom like in Gradius? Are you going to spend the whole frame in an NMI handler waiting for sprite 0?

Posted: Sun Mar 28, 2010 12:05 pm
by Dwedit
Time Lord uses a DMC IRQ to assist the bottom status bar if the CPU usage of the game is too heavy.

Posted: Sun Mar 28, 2010 12:18 pm
by tokumaru
Hey tepples! Hey tepples! Maybe a split is in order huh?
tepples wrote:I seem to remember it's good enough for Square.
See , I don't get this. People is this scene always seem content to do something a certain way because some commercial game did it. How well do you think programmers back then knew the NES? We know for a fact that the companies back then didn't receive much support from Nintendo, and being companies they were just interest in getting games out and money in, doing things right was not a primary concern. Back then they would give programmers that never made a NES game before a crappy manual in engrish and expect them to come up with a finished game in a few months.

Now look at us. We've studied this freaking console inside and out, and had several years to digest that info. We know more than most coders did back then. There is absolutely no reason for us to be content with mediocre solutions we know are flawed.
If your music is slowing down because you miss vblanks, your game is slowing down too, and you should either optimize your code or just do a Micronics and take the whole thing down to 30 fps. Otherwise, you'll have people complaining that they missed the jump because the frame rate changed.
You know it's not just the music. Like I said, status bars are very important too. As are scanline counters (you certainly don't want IRQs firing at the wrong times). Face it, there are several things that should be given high priority, and you just can't do that by simply waiting for VBlank.

And you are talking about optimizing and such, but you should know that in certain types of games it doesn't work like that. Or maybe you don't know, because so far your programs have always used a controlled number of game entities. But when you have dynamic entities they might end up accumulating, despite the effort you put into level design so that it doesn't happen too often. If by any chance you decided to optimize for the worst case where 20 enemies are on-screen, these enemies would probably not be more smart than balls moving left and right. And the game would be specially dull with only 2 of them on-screen.

Slowdowns have always happened, and still do to this day, even on computers with GHz of CPU power and powerful GPUs. The more dynamic a game world is, the harder it gets to control how much of it is active at any given time.
For newbies, I just think a Square-style solution with a direct counterpart to vsync() in Allegro is easiest to understand.
It might be, but I kinda don't see the point in teaching something that will require them to modify the architecture of their code (something that is never easy) because this simple solution might break with something as simple as a status bar.
Define a "break". A split-thread solution will "break" if you try to upload an inconsistent data set to the PPU, and I can think of situations where it would break hard if you don't know semaphores.
A simple "frame is ready" flag solves all the problems. Don't use the data unless it's flagged as consistent, it's not hard.
There are two loops here. The first waits for end of vertical blanking. The second waits for the sprite 0 hit on scanline 30 and prepares to set the X scroll position. The @s0giveup is a safety feature: if sprite 0 fails, it won't freeze like SMB1; instead, it will flicker a bit and continue.
Yeah, so much for a simple solution... And it even comes with exclusive built in Visual Glitches™. SMB freezes? That sucks. See why we can't think of the old programmers as gods?
And what happens if your status bar is at the bottom like in Gradius? Are you going to spend the whole frame in an NMI handler waiting for sprite 0?
Then you have to adapt. If in the end you can't think of a way to fix it, go with what you know will work well. Now, knowing how to fix something and not doing it, that's stupid.

Posted: Sun Mar 28, 2010 12:42 pm
by Bregalad
SMB1 never freezes, but when it lags (go somewhere with two hammer bros to figure out) the music slow downs and the status bar shakes horizontally.

What happen is that it does it like that :
* A NMI fires and the game enter in it's game handler
* Do sprite DMA (so sprite 0 is correctly placed)
* Wait sprite-zero hit, set correct scroll
* Do frame logic (which takes too long)
* No NMI fires, but the PPU starts to draw a new frame with the status bar wrongly placed, and sprite zero hit might not occur - but it won't freeze as the game don't look for it
* Frame logic terminates and exit

It is bad that the status bar shakes and the music slows down, BUT it never freezes. The only games that I know that freezes is Castlevania (U) (PRG0), and I have no idea if it is because of sprite Zero hits or slowdowns.

Posted: Sun Mar 28, 2010 12:49 pm
by tepples
tokumaru wrote:You know it's not just the music. Like I said, status bars are very important too. As are scanline counters (you certainly don't want IRQs firing at the wrong times).
A lot of the time, resetting a scanline counter IRQ either doesn't need any registers, or it only needs A, not X and Y. I agree with you that those should go in the NMI handler even under a simple wait-for-vblank setup.
But when you have dynamic entities they might end up accumulating
SMB1 has "optional" enemies that don't show up if the critter table is full.
If by any chance you decided to optimize for the worst case where 20 enemies are on-screen, these enemies would probably not be more smart than balls moving left and right.
A critter doesn't have to be any brighter than a ball 90 percent of the time. It only has to think at points in its pattern where it has to react to something. The monsters in Pac-Man don't think unless either they're coming to a corner of the maze or Pac-Man just ate a power pellet. And here's where the 90 percent comes in: If you give each critter a full think cycle one out of every ten frames, you can give only two out of 20 critters a think cycle every frame and still come out ahead.

I should have my cousin ROM hack Super Mario Bros. to start at 8-1 and then randomly switch between 60 fps to 30 fps to show why slowdown isn't optimal. Missing a jump because more critters showed up is not fun.
And the game would be specially dull with only 2 of them on-screen.
Not always: Two characters, one legendary franchise.
A simple "frame is ready" flag solves all the problems. Don't use the data unless it's flagged as consistent, it's not hard.
Do you care to write a wiki page about a game loop that uses such locking?

Posted: Sun Mar 28, 2010 12:50 pm
by tokumaru
I don't think there are excuses for making a game prone to freezing and/or shaking/flickering if you know how to fix it. I don't know about you, but I'm pretty sure I have some level of OCD that makes me extremely sensitive to visual glitches, I just can't stand them. I think they scream "lazy programmer", because in most cases they can be avoided.

Posted: Sun Mar 28, 2010 1:04 pm
by Bregalad
Sometimes I wonder how programmers tested their games.

For example look in Mega Man 3 in Top Man's level there is a place where there is 2 trax mettaurs, and the game ALWAYS drop framerate and flicker here. Capcom testers really COULD have noticed that there was something wrong and remove one of them or do something so that it don't look that horrible.

The worst is without a double Parodius. As soon as you have one option or so, the game CONSTANTLY slows down and just sometimes go back to 60 FPS when there is no enemies on screen, it's barely playable. What were Konami thinking ? Couldn't they go back improve their routines and/or make less enemies on the screen until the game is playable ?

I mean it's not bad to have a good way to handle slow downs, but it's even better if you manage to avoid slow downs altogether. For example Parodius probably handles slow downs fine, having the music play a good speed and the status bar appear fine, BUT it is still really annoying to play. I'd rather have it almost never slow down and have its status bar shake in the very rare case it slows down like SMB or Double Dragon.
You'd still want to test a slow down case, just to make sure the game don't crash tough.

Posted: Sun Mar 28, 2010 1:05 pm
by tokumaru
tepples wrote:A lot of the time, resetting a scanline counter IRQ either doesn't need any registers, or it only needs A, not X and Y.
Does that even matter? Can't you just backup and restore?

I consider status bars as important as IRQs. The jumping/flickering effect resulting from lag frames are very similar in both cases.
I agree with you that those should go in the NMI handler even under a simple wait-for-vblank setup.
But wouldn't you agree that once you start mixing things all the simplicity is gone?
I should have my cousin ROM hack Super Mario Bros. to start at 8-1 and then randomly switch between 60 fps to 30 fps to show why slowdown isn't optimal. Missing a jump because more critters showed up is not fun.
Like I said, I don't think slowdowns should be a constant thing in a game. Of course you should try hard prevent them, but you should still be prepared for them. AFAIK, there is no 100% safe way to avoid slowdowns, and I'm kinda against wasting time to manage time, to me this is a paradox (if you didn't waste time managing time there could be enough time for you to not need to manage it).
I'm sure they have more than 2 active game entities. There are things in the background you can break, people riding bikes, energy attacks and miscellaneous effects adorning the screen, etc.
Do you care to write a wiki page about a game loop that uses such locking?
Sure. Let me know what's the best place for it and I'll try to format it accordingly.

Posted: Sun Mar 28, 2010 1:19 pm
by tepples
Whenever you use a calendar or a day planner, you are "wasting time to manage time".

The background animations in SF2 are as dumb as balls, as are the projectiles.

Anyway, I think I now understand the technique well enough that I started on such an article, linked from the programming guide: NMI thread

Posted: Sun Mar 28, 2010 1:38 pm
by tokumaru
tepples wrote:Whenever you use a calendar or a day planner, you are "wasting time to manage time".
...hence why I don't! Really, I am a terribly disorganized person. It has worked out fine so far! =)
The background animations in SF2 are as dumb as balls, as are the projectiles.
I know. But it's still more than 2 dumb balls like in the scenario I suggested in the other post, since at least the fighters are somewhat intelligent.
Anyway, I think I now understand the technique well enough that I started on such an article, linked from the programming guide: NMI thread
Great. Do you mind if I make some adjustments/additions? I don't think it's very newbie friendly as it is, even I got confused a couple of times while reading it. I bet that more than 50% of the people that will read that page don't even know what a "thread" is.

EDIT: Does the wiki have a "status bar" page? The page about the NMI thread might be confusing if what makes a status bar isn't clear for the reader.

Posted: Sun Mar 28, 2010 2:35 pm
by tepples
tokumaru wrote:Great. Do you mind if I make some adjustments/additions? I don't think it's very newbie friendly as it is, even I got confused a couple of times while reading it. I bet that more than 50% of the people that will read that page don't even know what a "thread" is.
Go ahead. I added "thread" to the list of general CS topics.

Posted: Sun Mar 28, 2010 3:41 pm
by Orsi
Maybe I'm out of my league here, but I'd like to comment on this statement:
Why do you keep teaching people your method (I know you are proud of it) when you know it's flawed under certain circumstances? It can be slightly friendlier to newbies who are used to polling $2002, but in the long run everybody wants to expand their games, so why teach them something that could break the games once they reach a certain point of complexity?
Newbies are newbies. They just want to see progress in what they are doing, and they cannot make progress if they are worrying about things such as optimization, best practices or standards which they cannot even understand yet. If they are constantly being corrected, told something is wrong, or are provided examples and solutions which are beyond their comprehension the slow progression will eventually deter them from even continuing. This goes for any project or task, not just learning how to program. Too much focus on what's not working in the best possible way takes away from what is actually working.

At some point, if someone is determined enough to make such a complex game, a newbie would become experienced enough to actually understand why another way would be better. The peculiarities of syntax or semantics is and should not be their main concern when just learning a language. No one learning English would learn it if they were told that they pronounced or formed every sentence improperly, and that it would be better in the long run if they corrected themselves now. That sucks the enjoyment of learning right out of everyone.

This is especially important for this community and the computer world in general. Every guide, tutorial or wiki for programming in NES is waaay too concerned with best practices, technical terms or proper functionality. The truth is, those who are actually motivated to do something worthwhile will learn those things as they go whether or not it's drilled into them from the beginning. Placing so much emphasis on specifics and exceptions, instead of an overall general understanding, will only deter many people from even attempting to learn in the first place.

If you guys truly wish to make NES development a happening scene, something which I would LOVE to see happening now that I've stumbled upon it, I strongly urge you to stop worrying about such small things when helping others. I, and other newbies, are learning because of enjoyment and one cannot enjoy learning by being taught small things which are insignificant to our current level. Lighten up! Those who choose not to correct their code and continue with bad coding practices are most likely not going to make anything grand anyways!