Help with noob problems in my noob code?

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

Moderator: Moderators

User avatar
Banshaku
Posts: 2404
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

I didn't check everything but one thing, in your routine file, at the end of NMI, you do "JMP RTI". The JMP is not necessary, only RTI.

At the end of an interrupt routine or sub routine you need to tell the assembler that you need to go back to the point where it was called. For interrupt you use RTI (return from interrupt) and from sub routine you use RTS (return from sub routine). What it basically does is that it "return" to the address before the NMI or sub routine was called.

As for the reason that it goes haywire, all your logic is called from the NMI. This is a big no-no on the nes. NMI should be only used to do critical things like updating the screen. This logic should be done outside the nmi, in your game loop. For now, your code doesn't have any: there is only a forever loop. If you want your code to work properly, you will need to put the logic at the right place. Of course this is something you cannot guess from the first time you write code on the nes.

One approach is to put a flag that is updated in NMI (a counter for example), once NMI is over, the counter will be updated. In the "forever" loop, you wait that the coutner change. Once this is done, this is when you can do your collision detection logic.

I'm concerned about this comment thought:
Please keep in mind as you read this (and especially if you look at my amatuerish code in the .asm files) that I am a VERY green noob and not a programmer.
Do you know about the basics of programming at the least? If not, this will make you life quite a living hell by starting to program on the nes. The nes is not very forgiving if you make any mistakes. I don't say that it's not possible, some people here did and succeeded but if you really don't have any background in programming, I would suggest that you read a little bit about the fundamentals like what are variables, loops etc. That will help you in the end.
User avatar
MetalSlime
Posts: 186
Joined: Tue Aug 19, 2008 11:01 pm
Location: Japan

Post by MetalSlime »

bigjt_2 wrote: PPS- Oh yeah, I also forgot to mention that I've noticed that annoying grey tile in the upper left-hand corner. For the life of me I can't figure out why the hell that thing is there. I know it's something I did, but I've just been kicking it down the road in regards to fixing it.
I haven't had a chance to look at the code in detail yet (will do once the kid goes to bed), but I know why you are getting the gray tile. Actually, you have something like 60 gray tiles there.

For unused sprites we normally set their location to somewhere offscreen. Setting a sprite's Y value to something greater than $F8 will do this. Your reset code fills the $300 page of RAM with $FE (see the "clrmem" label), but then your sprite code uses the $200 page of RAM for sprites. $200 gets filled with $00 in your reset code, so all of the sprites are set to X=0, Y=0 - the top left of the screen.

To fix this, edit your clrmem code and have it write $FE to the $200 page of RAM instead of the $300 page.


edit: the reason collision_subroutines.nes is crashing in Nintendulator/Nestopia is you have a line in your code that isn't indented. The RTS command in RightCollisionTest needs to be indented. Fix that and the program runs fine.


And as Banshaku said: don't JMP RTI. Just stick an RTI command at the end of nmi, and get rid of the RTI: RTI label you have.

He's also right about logic code in the NMI - most people try to avoid that. For a small program like this it probably won't matter. But when you start writing more complex programs you will probably want to reserve NMI for drawing and put game logic in the main loop outside NMI.

Disch wrote a great document about frames/NMI that you should read. It might not make sense to you yet, but if you reread it from time to time as you gain more experience programming the NES it will all come together eventually. I think its found here, although I'm having some trouble viewing it correctly right now: http://sites.google.com/site/nesdevhand ... frame.html
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

MetalSlime wrote:He's also right about logic code in the NMI - most people try to avoid that. For a small program like this it probably won't matter. But when you start writing more complex programs you will probably want to reserve NMI for drawing and put game logic in the main loop outside NMI.
There are three ways to structure your code:
  • Like Super Mario Bros., with everything after the init code in the NMI handler; main thread is just "forever: jmp forever"
  • Like Final Fantasy and LJ65, with everything in the main thread and an NMI handler that just sets a flag that NMI occurred
  • VRAM update code in NMI, everything else in main thread
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

Banshaku wrote:
Please keep in mind as you read this (and especially if you look at my amatuerish code in the .asm files) that I am a VERY green noob and not a programmer.
Do you know about the basics of programming at the least? If not, this will make you life quite a living hell by starting to program on the nes. The nes is not very forgiving if you make any mistakes. I don't say that it's not possible, some people here did and succeeded but if you really don't have any background in programming, I would suggest that you read a little bit about the fundamentals like what are variables, loops etc. That will help you in the end.
Yeah, actually I've done programming for years here and there, but only as a hobbyist. It's never been something I've studied in school or -obviously :-) - done as a profession. The basic concepts of constants, loops, variables, addresses, boolean logic I'm fairly well versed in. However there are some that are still eluding me and I am reading up on them.

I'll work on moving the logic and figuring that out. Thanks for the help, folks.
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

MetalSlime wrote: edit: the reason collision_subroutines.nes is crashing in Nintendulator/Nestopia is you have a line in your code that isn't indented. The RTS command in RightCollisionTest needs to be indented. Fix that and the program runs fine.
Of course it would be something that simple I overlook. :-/ Thanks, metalslime.
metalslime wrote: And as Banshaku said: don't JMP RTI. Just stick an RTI command at the end of nmi, and get rid of the RTI: RTI label you have.

He's also right about logic code in the NMI - most people try to avoid that. For a small program like this it probably won't matter. But when you start writing more complex programs you will probably want to reserve NMI for drawing and put game logic in the main loop outside NMI.

Disch wrote a great document about frames/NMI that you should read. It might not make sense to you yet, but if you reread it from time to time as you gain more experience programming the NES it will all come together eventually. I think its found here, although I'm having some trouble viewing it correctly right now: http://sites.google.com/site/nesdevhand ... frame.html
Definitely will take a look at it today. This is something I'm still having trouble on. Thanks again, guys.
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

tepples wrote: There are three ways to structure your code:
  • Like Super Mario Bros., with everything after the init code in the NMI handler; main thread is just "forever: jmp forever"
  • Like Final Fantasy and LJ65, with everything in the main thread and an NMI handler that just sets a flag that NMI occurred
  • VRAM update code in NMI, everything else in main thread
Yeah, I actually found the SMB disassembly on ROMhacking.net (I believe you were the one that clued me in to its existence, tepples -nice!) and I've been looking at it. But obviously it's waaaaay beyond anything I'm doing now. I can understand fragments of it but here and there I start feeling like I'm reading heiroglyphs. It's a pretty valuable learning tool, though. Again guys, danke! I really appreciate you generously giving me your time here.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

tepples wrote:
  • Like Super Mario Bros., with everything after the init code in the NMI handler; main thread is just "forever: jmp forever"
If by any chance processing the logic of a frame takes longer than a frame, you'll either have an NMI firing on top of the other (which will mostly crash your game) or you'll miss the vertical blank altogether, which will cause the whole game to lag.
  • Like Final Fantasy and LJ65, with everything in the main thread and an NMI handler that just sets a flag that NMI occurred
The game won't crash if the logic takes too long, but the whole thing will still lag.
  • VRAM update code in NMI, everything else in main thread
Here you have the option to update some stuff even if the frame logic isn't finished yet, like the music, so that even though the gameplay lags the music doesn't (this seems to annoy players less than when everything lags). Also, your game logic can prepare data for different types of updates gradually, so that the NMI can use whatever is ready when it fires (my raycaster works like that), making better use of the available time.

Conclusion: only use the first two options if your game logic is guaranteed to finish in less than one frame's time, otherwise you'll have some nasty stuff to deal with (specially if you rely on timed code or sprite 0 hits for status bars and things like that).

Also, if you do go with the first option, make sure to perform VRAM updates first, and then compute game logic, because the NMI fires when VBlank starts, and it would be a waste to use precious VBlank time with logic that is not updating the video.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

If by any chance processing the logic of a frame takes longer than a frame, you'll either have an NMI firing on top of the other (which will mostly crash your game) or you'll miss the vertical blank altogether, which will cause the whole game to lag.
SMB and Zelda will lag, including the sound, and SMB's status bar will shake horizontally.

Konami games, however, that are also structured like that, are designed in a way that a NMI can interrupt another without the game to crash. I have no idea if crashes of Castlevania (PRG0) are related to this or not - but Gradius games slow down a lot and never crash.

Even if you use the balanced option - your game might crash/get bugs if you don't proprely save the registers and separe the variables you use in the interrupt and the ones you need in the main thread. This is the kind of stuff you can work on an entiere games that everything works well but the day it lags it crashes or something and you won't understand why.
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 »

Bregalad wrote:SMB's status bar will shake horizontally.
Exactly, that's terrible. If it misses the NMI it can't properly place the status bar, that's a very poor design choice. I always mention music as one of the critical things that should be updated even during lag frames, but status bars are definitely important too. I'm seriously annoyed by shaking status bars.
Konami games, however, that are also structured like that, are designed in a way that a NMI can interrupt another without the game to crash.
You can code a game like that and have the NMI know if it interrupted another by using flags to indicate that, and in case it did interrupt it just takes care of the critical stuff (music, status bar). But if you think about it, this is just a simulation of the case when there's a main thread and an NMI thread. Why not use the real thing instead of simulating it?
I have no idea if crashes of Castlevania (PRG0) are related to this or not - but Gradius games slow down a lot and never crash.
Games would most likely crash if a new frame's logic started being calculated before the previous one ended. Variables would be overwritten, the stack could overflow, and the game would basically find itself in an inconsistent state. This is something no programmer wants, and a bug that might be hard to catch because the game ran fine before (when less computation was done before you added a new feature). That's one of the reasons I'm against this set up.
Even if you use the balanced option - your game might crash/get bugs if you don't proprely save the registers and separe the variables you use in the interrupt and the ones you need in the main thread.
Sure, no matter what you do you can always screw it up! =) But to me the idea of a main thread separated from the NMI thread is more logical in case your game logic might take more than one frame (which is hardly the case for static screen puzzle games, so these usually get away with anything).

Having the NMI set a flag is OK in my book, as long as you're sure the game logic takes less than a frame to complete. Having everything in the NMI on the other hand is the worst design choice a NES programmer can make. Not only it has the potential for causing disastrous results in case of lag, but the whole arrangement of taking care of graphical updates before taking care of the game logic is kinda awkward.

You know, you have to first process a frame worth of game logic so that you have something to update the screen with, but if you have everything inside the NMI you have that in reverse order, so you're gonna need some special handling for the first frame or you'll have to use a flag to indicate whether a frame has been processed, and if you have that you are just simulating the other method, like I mentioned before. I see absolutely no point in having everything in the NMI.
This is the kind of stuff you can work on an entiere games that everything works well but the day it lags it crashes or something and you won't understand why.
Exactly. That's a nasty kind of bug, because one minute everything is working well, but then you add this tiny new little feature and all hell breaks loose. It will take you forever to realize it was a timing thing, rather than a problem with the new feature.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

tokumaru wrote:Here you have the option to update some stuff even if the frame logic isn't finished yet, like the music, so that even though the gameplay lags the music doesn't (this seems to annoy players less than when everything lags).
If the code signals NMI by adding 1 to a counter (which is dead easy on 6502), it will increment the counter twice, and the main thread can detect this and run music twice. Pokemon Blue's music often gets behind by five or six frames while things load into the Game Boy's VRAM, but it always catches up.
Also, your game logic can prepare data for different types of updates gradually, so that the NMI can use whatever is ready when it fires
Which means you have to learn how to use semaphores so that your VRAM update logic never sees any half-finished buffers. If you can write a tutorial for the wiki on splitting updates into main and NMI threads, and you can remember to cover the various locking scenarios, I'd appreciate it.
Conclusion: only use the first two options if your game logic is guaranteed to finish in less than one frame's time, otherwise you'll have some nasty stuff to deal with (specially if you rely on timed code or sprite 0 hits for status bars and things like that).
If your sprite 0 is near the bottom, you'll have problems even if you do update VRAM in an NMI thread. That's why (if I recall correctly) Gradius counts the number of "things" it does in a frame and then waits for sprite 0 if it needs to do more than the number of "things" it can safely do in a frame.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Why not use the real thing instead of simulating it?
Well go ask Konami !
Games would most likely crash if a new frame's logic started being calculated before the previous one ended. Variables would be overwritten, the stack could overflow, and the game would basically find itself in an inconsistent state. This is something no programmer wants, and a bug that might be hard to catch because the game ran fine before (when less computation was done before you added a new feature). That's one of the reasons I'm against this set up.
Yeah, but again you can absolutely sure the program will never lag in some condition.

Having the NMI set a flag is OK in my book, as long as you're sure the game logic takes less than a frame to complete. Having everything in the NMI on the other hand is the worst design choice a NES programmer can make. Not only it has the potential for causing disastrous results in case of lag, but the whole arrangement of taking care of graphical updates before taking care of the game logic is kinda awkward.
Altough I agree with you that "everything in NMI" idea is questionable, I don't think it is a valid argument : you don't have to code the program in the same order as it is executed. Taking care of VRAM updates typeically is just emptying a few buffers if corresponding flags are set, and one of the first things you'd want to code anyway.

Personally I have tried all 3 : When I fist started with Nesticle and nesasm I did the "everything in NMI" thing because other programs I've investigated did that. Eventually I found it inconvenient and moved to "everything outside of NMI" philosophy. Again I found it inconvenient, and when I eventually started my real game project I opted for the balanced option and it works wonderfully well.
However this is also due that my programming skills and knowledge of hardware increased as I switched from a model to another.

For me, having everything in NMI is like having the AI of enemies starting always at the same adress... it's *possible* to do things that way, but as soon as you want to do something more complicated it will become a terrible headache because you'll need countless variables associated with if/else statements in your code and it will be VERY easy to screw up. Konami still managed to develop dozen of NES games this way, and very good ones.
That's why (if I recall correctly) Gradius counts the number of "things" it does in a frame and then waits for sprite 0 if it needs to do more than the number of "things" it can safely do in a frame.
Well since it doesn't use any kind of IRQs that's the only way it could do that, but I wonder how they did it. Chances are that they risk to either count too fast and lag when it could run without lagging, or count too slow and result in a shaking bar.
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 »

tepples wrote:If your sprite 0 is near the bottom, you'll have problems even if you do update VRAM in an NMI thread.
Sure, but this is a special case of "early VBlank", and the NMI can only warn you about regular VBlanks, not early ones. I guess this is why status bars at the bottom of the screen only became common once scanline counters were widely available.

The solution used by Gradius is very sub-optimal though, because the game wastes time figuring out if it can use more time, which is a paradox. If it didn't have to check, there would be more free time, but not necessarily enough. Let's agree that even though it's better than visual glitches, it's still not a very good solution.
Bregalad wrote:Altough I agree with you that "everything in NMI" idea is questionable, I don't think it is a valid argument : you don't have to code the program in the same order as it is executed. Taking care of VRAM updates typeically is just emptying a few buffers if corresponding flags are set, and one of the first things you'd want to code anyway.
You are right, but my point is that since you are using flags to control what gets updated and what doesn't anyway, it would make more sense to clearly separate the 2 threads than awkwardly pack them together.
However this is also due that my programming skills and knowledge of hardware increased as I switched from a model to another.
Yeah, the whole concept of interrupts is hard to grasp for a beginner, and for them it's probably easier to code it all linearly.

The problem is that many tutorials would rather teach what gives quick results than best practices. It may look like a good idea at first, but in the long run it ends up confusing people.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

tokumaru wrote:I see absolutely no point in having everything in the NMI.
Really, the only point is so you can perform the code defined in the NMI routine one time every frame. It just regulates the speed at which your code is executed. This is what some see as the easy way out of setting up a main loop that's executed once every frame by waiting for the next frame at the end of the loop. But once you figure it out, it's pretty uncomplicated:

Code: Select all

NMI:
 ;Save A, X, and Y
 ;blah code
 ;Restore A, X, and Y
 inc VBLCount
 rti

MainLoop:
 ;blah code

 lda VBLCount
-
 cmp VBLCount
 beq -
 jmp MainLoop

So once you see the simplicity of setting that up, you're right, there's really no advantage to putting your code in the NMI routine. I think it's just being lazy. If you're making a simple test program, it's fine. If you're making a game like a platformer or something that has to work with AI and all sorts of decompression, it would be catastrophic.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

I agree 100% with celius, but yet I just wanted to point out that Gradius, Contra, Super C, Castlevania 1, 2 & 3, Lagrange Point, etc.... ALL does everything in NMI, so this is possible. I'd still recommand doing what Celius says for standard applications, but in some cases if you do crazy raster effects and want to synchronize with NMI (instead of sprite zero hit) you're pretty much forced to do otherwise and have your raster effect in NMI.

However, it's rare in an actual game you'll find yourself writing to $2005 every scanline or so - but for example Marble Madness which writes to $2000 twice every scanline to display text was forced to do everything in NMI like early Nintendo and Konami games.

So the "ultimate" solution is to do what Celius says, but if you want to do something special like crazy raster effect have a flag in the NMI routine that makes it execute another portion of code instead of the normal NMI so that everything is timed etc...
Useless, lumbering half-wits don't scare us.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Post by thefox »

tepples wrote:
tokumaru wrote:Here you have the option to update some stuff even if the frame logic isn't finished yet, like the music, so that even though the gameplay lags the music doesn't (this seems to annoy players less than when everything lags).
If the code signals NMI by adding 1 to a counter (which is dead easy on 6502), it will increment the counter twice, and the main thread can detect this and run music twice. Pokemon Blue's music often gets behind by five or six frames while things load into the Game Boy's VRAM, but it always catches up.
Yeah so then it will not lag, it will "skip" which is equally bad in my opinion.
Bregalad wrote:I agree 100% with celius, but yet I just wanted to point out that Gradius, Contra, Super C, Castlevania 1, 2 & 3, Lagrange Point, etc.... ALL does everything in NMI, so this is possible. I'd still recommand doing what Celius says for standard applications, but in some cases if you do crazy raster effects and want to synchronize with NMI (instead of sprite zero hit) you're pretty much forced to do otherwise and have your raster effect in NMI.
Well, that's simply not true, it could have as well returned from NMI to the main loop which checks the "nmi flag". There's no upside to doing that though if you need raster timed code, so might as well have everything in NMI. (And there's a small downside: the NMI flag polling loop will result in a more variable delay depending what code is executing when NMI occurs.)
Post Reply