How to wait for vblank

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

tokumaru wrote:
blargg wrote:Maybe I didn't read your objection carefully. I thought you were suggesting an NMI handler that did more than increment a counter, as something to teach a new programmer before the model tepples described.
This was my original suggestion. I don't think it's too overwhelming.
Agreed. Your example also shows the simplicity very well.
IMO what goes on in the NMI is hardly multi-threading [...] They must understand that the main program WILL be interrupted at certain points in time and they must write a separate piece of code to handle that situation.
Which is what multi-threading is. Any shared data structures must be updated atomically. Basically you must use lock-free techniques.
I did object to the dissemination of a piece of code that I considered flawed
Flawed, bad, same thing. It's only flawed if it doesn't meet the requirements. If the game were doing a status bar or whatever, or did VRAM updates and could fall to 30 FPS sometimes, then I agree it would be a flawed approach since it would glitch. If it won't glitch, AND it reduces complexity, I call that success, not flawed.
Some of them [NES newbies] come from other systems or languages, and have enough knowledge to experiment with the "difficult" stuff, so I think that information should be offered somewhere.
Absolutely. I at least have a vision of NES tutorial materials covering the spectrum. BTW, you referred to "clueless newbie". The only kind of clueless newbie I envision is one whose natural learning capacities have been destroyed by compulsory "education", and who now learn by rote. I always try to counter this by encouraging involvement and experimentation, so that deep understanding can be achieved. Even deep understanding of LDA immediate is notable in my book.
I DID say tepples' was bad because of it's limitations, but I DIDN'T say the other method was the best, I just mentioned it didn't have the same shortcomings.
Everything has shortcomings, and those are liabilities in some contexts. In others, they are not a problem. If these shortcomings are due to benefits the approach also has, then it will be superior in some contexts. If you have a way that is just as good or better than tepples' in all aspects, THEN I won't object to you offering it as all-around better.
Since there seems to be some kind of consensus that tepples' way to handle VBlanks is the easiest one for newbies, I'll just have to shut up on this one, even though I don't agree with it. But I'm sure there will eventually be people asking "why is my status bar jumping", if these newbies persist enough to make a scrolling game.
I'm all for discussion of the good and bad aspects of an approach, especially in a particular context, and comparisons to other approaches. But I'm against "this is bad, get rid of it" kinds of "discussion".

Your main argument for "your" approach seems to be that it will handle more advanced game designs better. But why not argue for an even more complex one, since it can handle even more than yours? I think tepples' approach is better than yours IN THE CONTEXT of people learning NES programming, up to the part where they do status bars, interrupts, etc. That's a lot of ground between.
Another thing I happen to not agree with is clearing the whole memory to 0. Say, if a person forgets to initialize a variable before starting a level, but everything works fine because the variable was cleared at the beginning of the program. Now, when the second level starts and the variable is no longer 0, something goes wrong. Wouldn't you say this bug would be harder to catch, since the first level worked fine but the second one didn't?
Again, I'd love to have a discussion of the relative merits of each approach, without one side believing that his approach is better and that he simply needs to make the other believe the same. Even though I prefer clearing, I am open to discussion of both. Ideally, we'd explore the issue from a fresh perspective and consider all the approaches, even beyond clearing or not clearing. In your case, the bug would be easy to find: start the first level, let the player run through it, then start it again. If it behaves differently the second time, you forgot to initialize something.
[...] I don't think I'll even abandon NESDEV, even when I'm 80 years old or so. I just hope that by then someone will have successfully made an accurate NES clone, otherwise we'll probably have to rely solely on emulators... =)
That's a scary thought... I wish you hadn't mentioned that. A time when there are no more working NES units? *shudder*
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

blargg wrote:Which is what multi-threading is.
Yes, but it's such a simplified version of it that it might be confusing to bring the term up right from the start. IMO there should be more like a note at the end of the page explaining the technique that says "this is a simple form of multi-threading, if you want to know more, go to wikipedia".
Flawed, bad, same thing. It's only flawed if it doesn't meet the requirements.
The fact that I consider it flawed is probably just a consequence of me designing the kinds of game I like to design.
The only kind of clueless newbie I envision is one whose natural learning capacities have been destroyed by compulsory "education", and who now learn by rote.
And wouldn't you agree that there are a lot of those? Some people simply want results, without caring much for what's behind those results.
Even deep understanding of LDA immediate is notable in my book.
I agree.
I think tepples' approach is better than yours IN THE CONTEXT of people learning NES programming, up to the part where they do status bars, interrupts, etc. That's a lot of ground between.
You are probably right, I shouldn't worry about this. When programmers get good enough to the point of adding those features they should be able to understand WHY a certain method would fail when it finally does.

I seem to remember we've had newbies here before asking for the recipe of a status bar though. But they probably wouldn't have any problems with lag frames anytime soon.
In your case, the bug would be easy to find: start the first level, let the player run through it, then start it again. If it behaves differently the second time, you forgot to initialize something.
Yes, it's still debugable, but I feel like it encourages poor variable management. I think that newbies should be trained to pay attention to their variables, and if you spoil them by giving them zeroed out memory they will fail to assimilate how important initializing variables in assembly language is. IMO, that's even a poor simulation of what some high level languages do, because they also clear local variables, and in NES code I have never seen anyone bothering about clearing the variables used by each subroutine.
A time when there are no more working NES units? *shudder*
Really scary. Fortunately there are people working hard to clone it, let's just hope they succeed! =)
User avatar
Banshaku
Posts: 2404
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

@Blarg and Tokumaru

For the "clearing memory is evil", I don't know. I always initialize my variables so this pre-initialization doesn't seems to break anything for me. When you are in a early stage with a few variables, using a debugger and check the ram location, sometime the empty spot helps to find that you put N value a the wrong place because of the 00 values everywhere. It it was not initialized, sometime I would have never found those bugs has a beginner.

As for the "only the counter in the NMI", I never went that way. From the beginning, I was expecting that I may have to produce a lot of data that will take time to decode so I went with flags and updating the content in NMI on demand. It felt natural to me. And one extra thing I added recently it to put the address of the NMI in RAM so I can change the address of this function "on demand" but I would not suggest that for beginner. This is just some test code and it's working fine when I need a completely different NMI function.

edit:

The argument about cleared memory could make it easier with a debugger is weak so I guess we should pass this one. For now, either way doesn't seems problematic. I just personally prefer to put all memory to zero out of habit.

edit2:

Removed comment about lj65/concentration room since the structures of files changed. Comments could have increased since the last time I saw the code and my remark could be not appropriate.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

tokumaru wrote:
blargg wrote:Which is what multi-threading is.
Yes, but it's such a simplified version of it that it might be confusing to bring the term up right from the start.
I wasn't saying it should be presented as such, just noting that because it IS a form of multi-threading, that such issues as atomicity and inconsistency arise, and must be dealt with.

I was thinking some about a NES tutorial. It'd start with CPU instructions, then move to graphics. At that point, you'd be doing many that wait for NMI, move things, then repeat. You'd start out with the minimal NMI that increments a counter, and explain how that works. Later, you'd start doing too much in a frame and show how this causes glitches. That would set the stage for expanding the NMI routine to do more than increment the counter. At that point, the student wouldn't have been burdened with threading issues, and would see a clear need for something beyond the single-threaded structure. You could then move the graphic update to NMI, but have the main thread update the shared data non-atomically, so that you occasionally got a different glitch. Then you could introduce the notion of atomic updates and why they're critical. Again, the student would see the clear need for it, rather than simply being told about it but not seeing it in practice. If I ever work on some NES tutorials, my main goal will be to do as much as possible with the things taught so far. This allows the student to have fun doing things with his knowledge so far. Since I'm doing more with less, he has more of a chance of mastering those few things he knows so far, before he moves on.
The only kind of clueless newbie I envision is one whose natural learning capacities have been destroyed by compulsory "education", and who now learn by rote.
And wouldn't you agree that there are a lot of those? Some people simply want results, without caring much for what's behind those results.
A person who just wants results doesn't want to learn. This type of person is never my audience. You can't write NES games via a recipe approach.
I think that newbies should be trained to pay attention to their variables, and if you spoil them by giving them zeroed out memory they will fail to assimilate how important initializing variables in assembly language is.
Yes, this is a good point in favor of not clearing. This is in favor of filling with random garbage that differs each run, in fact. Similar to tepples' shuffling idea for avoiding assumptions about variables' relative addresses.

One of the strong reasons I clear all RAM is that it greatly reduces the unknowns. If you don't, your program may run fine in all the situations you test it in, but there are nearly 2^16384 possible starting states of RAM that you haven't tested. By clearing, you greatly reduce the possible states.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Clearing RAM isn't always a bad thing. Lots of my variables are accumulative values, meaning they SHOULD start from 0 and accumulate, like the Vblank count (well, this isn't necessarily something that has to start at 0, but it is accumulative). A really good example is like the RAM for my sound engine. It contains a lot of indexes that are used to index music data, starting at 0, moving higher as the music plays (also there are indexes for volume envelopes). Not only that, it contains virtual registers which need to start out as 0, because my music engine sets bits of them individually. Since all 8 bits of a virtual register aren't updated every frame, and then they're copied to the actual sound registers, I cannot start out with a "random" or unknown value in them. It has to be initialized to 0.

There are many many variables that act like this in my code, and most of them are grouped together, so I can use a nice little loop that clears them all. Don't get me wrong, some values aren't supposed to be 0, in which case I don't necessarily need or want to clear them. But I also see Blargg's point, though it could be used as an argument against clearing RAM. If your code isn't working with a bunch of garbage in RAM, you're probably not properly initializing a value, in which case you'd like to find that out and fix the problem. But clearing RAM in that case is like a safety net. Should there be something you overlooked, you won't have something like a program crash because you know the exact values in RAM.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Celius wrote:Clearing RAM isn't always a bad thing. Lots of my variables are accumulative values, meaning they SHOULD start from 0 and accumulate, like the Vblank count (well, this isn't necessarily something that has to start at 0, but it is accumulative).
And will they always accumulate, the whole time the program is running? Don't you ever have to clear them at a certain point to start out "fresh" (like when going from one engine to another - main game to bonus game, for example)?
A really good example is like the RAM for my sound engine. It contains a lot of indexes that are used to index music data, starting at 0, moving higher as the music plays (also there are indexes for volume envelopes). Not only that, it contains virtual registers which need to start out as 0, because my music engine sets bits of them individually. Since all 8 bits of a virtual register aren't updated every frame, and then they're copied to the actual sound registers, I cannot start out with a "random" or unknown value in them. It has to be initialized to 0.
And don't you have to this on a per-song basis? Don't you have to do some sort of cleaning up/initialization every time a new song starts? Why not use this time to properly initialize all music-related variables?

I'm not suggesting people use the random values that might be in RAM, just that they initialize each variable as needed, as a form of guarantee that whenever the variable is used it will hold a valid value, as opposed to relying on the value it was given god knows how long ago by God knows what part of the program.

I often keep all my variable-resetting grouped by system, so each of the sub-systems in the game is responsible by initializing all of their variables, so I don't have to worry what was in RAM before. And it's not like clearing the memory only when necessary is slow or requires much code. I rarely need to clear arrays for example, because they usually have other variables that describe them. For example, a RAM slot used for dynamic objects doesn't need to have all the bytes cleared when an object is unloaded, I just have to flag the slot as empty. The next object that occupies that slot will initialize whatever it needs to, it won't have to worry about the trash left there by the previous object.
If your code isn't working with a bunch of garbage in RAM, you're probably not properly initializing a value, in which case you'd like to find that out and fix the problem. But clearing RAM in that case is like a safety net.
My point exactly. Would you rather have this safety net cover your ass and hide a mistake you made or would you rather make the error visible so that you could fix it? I'd rather detect every error I can.

I don't know if I mentioned this here before, but during development I include at the start of my programs the opposite of a memory clearing routine, I have a memory trashing routine. If fills RAM with numbers from my pseudo-random number generator. Whenever I find a "fishy bug" I change the seed of the PRNG to see if there is any change in the buggy behavior. It doesn't happen very often, but I believe I found a bug or two that way. Once I consider the program finished I remove that code.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

tokumaru wrote:
Celius wrote:Lots of my variables are accumulative values, meaning they SHOULD start from 0 and accumulate, like the Vblank count (well, this isn't necessarily something that has to start at 0, but it is accumulative).
And will they always accumulate, the whole time the program is running? Don't you ever have to clear them at a certain point to start out "fresh" (like when going from one engine to another - main game to bonus game, for example)?
Not if the definition of the variable is "number of vertical blanks since reset". There are separate timer variables for game states.
A really good example is like the RAM for my sound engine. It contains a lot of indexes that are used to index music data, starting at 0
And don't you have to this on a per-song basis?
They have to be initialized before the game starts playing a song. Otherwise, the program would start playing garbage between power-on and when the first song starts. Setting sound_effect_time_left[ch] to 0, instrument_time_left[ch] to 0, and tempo to 0 helps suppress this.
I'm not suggesting people use the random values that might be in RAM, just that they initialize each variable as needed
A lot of variables are needed from frame one, especially if you have an NMI routine that does more than just inc retraces. Such variables include vram_update_ready (0 = false), is_sprite_0_used (0 = false), and is_a_song_playing (0 = false). On a machine without unlimited space for init code, it can save space and time to define 0 to mean take no action, document this definition, and then spray zeroes.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

tokumaru, you make good points about putting random values during debugging, but once you're ready for a release, leaving memory uninitialized seems a bad idea. You have to assume you still have bugs, perhaps some of the kind that depend on uninitialized memory. By clearing in a release build, you ensure that these will at least behave consistently on every machine, rather than breaking on that one guy's NES whose RAM powers up in a significantly different state than most others.

It is true that many modules will be reinitialized multiple times in one session, being the ones that handle a level or similar. These can't rely on RAM contents, regardless of whether you clear RAM at power/reset. So if you took a bug-free game that assumed memory was cleared with zero and converted it to not assume so (and without simply adding a memory clear at the beginning :) ) I doubt you'd add many source lines, perhaps 50 at most.

So comparing the two, I see that yes, not assuming RAM contents allows you to use more debugging techniques, like random RAM filling, or even a NES emulator with something like valgrind, to find reads from uninitialized RAM. On the other hand, because it forces you to initialize every variable, it introduces more opportunities to forget initialization.

At this point I'm thinking that the type of NES program is the main determiner of which gives the most benefit. Virtually all my NES programming has been of a shell-like environment for running test programs, where there is little module reinitialization, and where conciseness is a virtue. In a game where there is little that is initialized only once, I could see random-clear during dev/zero-clear during release being a better approach.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

I don't think there is anything wrong or bad initializing the whole parts of memory. If you weren't doing it you would probably need to have a part of code that do something like this :

Code: Select all

 lda #$00
 sta variable1
 sta blahblah
 sta blabhblahbalh
 .....
 ;one tousand lines long
Ok you don't clear other variables which are cleared with appropriate routines (such as stop_music) or that simply don't need any initialization, and in all cases you have to initialise some varialbles with other values than $00.

But it's still a waste of bytes as opposed to simply have a loop that clears $000-$7ff and that takes ~10 bytes.
Useless, lumbering half-wits don't scare us.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

blargg wrote:So comparing the two, I see that yes, not assuming RAM contents allows you to use more debugging techniques, like random RAM filling, or even a NES emulator with something like valgrind, to find reads from uninitialized RAM.
Ah, I was wondering if something like this existed. Maybe this could be implemented in FCEUX for example?
Bregalad wrote:But it's still a waste of bytes as opposed to simply have a loop that clears $000-$7ff and that takes ~10 bytes.
I know it might seem more compact to clear everything at the start, but the thing is I don't think there are many variables that are initialized only once. Most of them need to be reset at one point or another (when a new level starts, when a new song starts, etc), so why not organize your code so that these resetting codes always act as the initializers of the variables, in a way that you wouldn't need a general memory reset? Looks much more organized to me.
Post Reply