JMP, fine...JSR, nope...

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

JMP, fine...JSR, nope...

Post by JoeGtake2 »

So this is the first time I've experienced this. It's sorta wacky.

I have a routine that I'd like to use in a few instances, so I set up a JSR->Routine->RTS scenario. It does not work. I mean, there are no errors returned, however its effect does not work...

Now, if I take the meat of the routine (all except for the RTS) and put it in line rather than JSRing to it, it works exactly as expected.

Also, I tried JMPing to it and then JMPing back to the next line (which would, for all intents and purposes, do the same as a JSR->RTS I would think), and that actually worked as expected.

I could do it in line, except that I'd like to use the routine in a few places and I'd have to change the label names...it just seems gratuitous to have to put the exact same code in multiple places like that; I had intended to just JSR to it from the few places that I needed it.

Can anyone think of a reason why it would work in line, JMPing to it and JMPing back would work, but cutting the exact code out, pasting it somewhere as a routine, and JSR-RTSing from it would not?

In context, I feel like this might be a fundamental thing I don't understand rather than something wrong with the code.

Thanks!
ROD
Posts: 7
Joined: Wed Dec 24, 2014 1:33 am

Re: JMP, fine...JSR, nope...

Post by ROD »

I had a problem like this not too long ago. I think it was because I did not push and pull my registers during NMI since I don't put everything in NMI. Maybe its that?

Just my noobish 2 cents.
thenendo
Posts: 28
Joined: Mon Oct 06, 2014 5:09 pm
Location: New Joisey (NTSC)

Re: JMP, fine...JSR, nope...

Post by thenendo »

In addition to possible interference by interrupt handlers, it's worth asking whether the meat of your routine touches the stack pointer or data on the stack at all.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: JMP, fine...JSR, nope...

Post by JoeGtake2 »

Hey guys - thanks.

I push all my stack values at the top of the NMI and pull them at the end. So that's not it...

But more importantly, nothing is being pushed or pulled during this routine.

I'm mostly perplexed that it works perfectly in line, but not as a routine.

Thanks for the suggestions though, definitely appreciate them. Any other thoughts?



EDIT: Actually, I am using a data pointer to pull some info during the routine. Could that have something to do with it? In which case, what exactly is the problem and how could it be remedied?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: JMP, fine...JSR, nope...

Post by tokumaru »

My only guesses are that there's something wrong with the stack or with the place you're putting this subroutine in the ROM.

Is the stack being properly initialized? Is the subroutine outside of the program flow? Is it in a bank that's mapped in when you call it?

Maybe if you shared some code or a ROM it would be easier for us to debug. This is definitely not common and shouldn't be happening.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: JMP, fine...JSR, nope...

Post by Kasumi »

JoeGtake2 wrote: EDIT: Actually, I am using a data pointer to pull some info during the routine. Could that have something to do with it? In which case, what exactly is the problem and how could it be remedied?
Pushes and pulls from the stack could mess with a subroutine, but reading from an indirect location shouldn't, no.

Code: Select all

lda #$FF
pha;one byte pushed to the stack
dostuff:
pla;the same byte (#$FF) is pulled off the stack
Versus:

Code: Select all

lda #$FF
pha;one byte pushed to the stack
jsr dostuff;this pushes the return address to the stack
returnlabel:

;Later in the code...

dostuff:
pla;This pulls half the address of returnlabel-1 off the stack rather than
;#$FF, because the address was pushed onto the stack on top of
;#$FF
rts;And, because of half the address has been pulled
;the two bytes the RTS uses as the return address 
;will no longer be the two pushed there by JSR

;Your program will probably return to a garbage location
If you're not touching the stack at all in the routine, it's not that. As tokumaru says, there's not much to talk about without seeing the code in question.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: JMP, fine...JSR, nope...

Post by JoeGtake2 »

No problem - here is some code. Hopefully it's enough context?

I have added a acceleration and deceleration method to my movement (works fine). Now I have to update my collision detection. This is where the problem is coming in.

Here is the code that works in line. For example, this reads the point directly to the right of the player's right bounding box, first at the top bbx, then at the bottom bbx, and evaluates the tile there for a collision. This works absolutely fine:

Code: Select all


TileCheckRight:
     LDA playerXright
     CLC
     ADC maxSpeedHi
     STA tempx

     LDA playerYtop
     STA tempy

     JSR GetTilePosition  ;; this reads from a collision table based on tempx and tempy
     JSR CheckCollision ;; for right now, this just checks solid or not solid (1 or 0)
     BEQ nextA
     JMP skipDec
nextA:
     LDA playerYbottom
     STA tempy
     JSR GetTilePosition
     JSR CheckCollision
     BEQ nextAA
     JMP skipDec
nextAA:

However, if I am to take that bit of code, turn it into a routine (i've tried putting it plenty of places just in sort of a trial and error way, and most I know have no conflicts as I can put other things in place there and JSR to them just fine), JSR->RTS to it (RTS coming right after the nextAA) , the values are not loaded.

Again, if I put it elsewhere and do something like this instead:

In the inline:

Code: Select all

     ;;;; the code leading up to it...
     JMP TileCheckRight
whatever:

and then in the routine:

Code: Select all


TileCheckRight:
     LDA playerXright
     CLC
     ADC maxSpeedHi
     STA tempx

     LDA playerYtop
     STA tempy

     JSR GetTilePosition  ;; this reads from a collision table based on tempx and tempy
     JSR CheckCollision ;; for right now, this just checks solid or not solid (1 or 0)
     BEQ nextA
     JMP skipDec
nextA:
     LDA playerYbottom
     STA tempy
     JSR GetTilePosition
     JSR CheckCollision
     BEQ nextAA
     JMP skipDec
nextAA:
     JMP whatever

...this ALSO works just fine, and the values are loaded and everything works as expected...

Does this help at all?


****EDIT, I've disabled all potential banks swapping in and out - so it's definitely not that. Everything in this code is loaded from the start and never unloaded.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: JMP, fine...JSR, nope...

Post by Kasumi »

So the RTS should be after nextAA?

Is there another RTS after whatever skipDec leads to? If not, you're jumping someplace and never actually returning from the subroutine.
thenendo
Posts: 28
Joined: Mon Oct 06, 2014 5:09 pm
Location: New Joisey (NTSC)

Re: JMP, fine...JSR, nope...

Post by thenendo »

Honestly what I would do is just run the rom in an emulator with a debugger like fceux or nintendulatordx, set a break-point on the routine, and step through it. You'll probably spot the problem immediately that way.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: JMP, fine...JSR, nope...

Post by koitsu »

Random things that come to mind:

1. Stack isn't initialised (i.e. ldx #$ff / txs and should only be done once during RESET) and/or cleared correctly (i.e. random crap is left in $0100-01FF that may be affecting certain behaviours depending on how your code actually works).

2. Some of your variables are referencing areas of RAM that are intended to be used by the stack, e.g. somevar eq $01ff with some code that modifies somevar (thus modifying the value at $01ff), but if the modification is done within the subroutine and then you call rts the PC that you get returned to is going to be different than what you expected (because you essentially modified the stack contents).

3. Bug in your code that is overwriting the stack when it shouldn't be. Common reasons I can think of for this would be using indexing operations that wrap pages (e.g. $00ff -> $0100) and you end up encountering Issue #2, or NMI routines periodically doing something to the stack area which they shouldn't. Pre-assembled values being calculated within the stack space would be determined through an assembly listing generation, but ones in real-time would require a debugger (see emulator). I would suggest looking through an assembly listing anyway, especially to make sure all of your variable accesses are zero page when they should be and absolute (16-bit) addresses when they should be (this matters if using modifiers that return the high byte or low byte of an address of something (a lot of code has to do this) -- and while that's done assemble-time, it can still have unexpected behaviours depending on what your goal is with your code).

4. Bug in your code where you're already using the stack but aren't pulling ("popping") values off entirely, resulting in a stack imbalance (more specifically: eventual stack underflow, I believe), e.g. pha / phx / phy somewhere, but then only doing ply / plx at the end of your routine (3 pushes vs. 2 pulls). Every time that routine would get called the stack pointer would decrement by 1 indefinitely and eventually bottom out (to $0100) and wrap back to $01FF.

5. A few other things that are in the back of my mind but would rather not discuss without ruling out all of the above.

It would really be more helpful if you could just post actual code snippets like "This works" {insert code block} "This doesn't" {insert code block}. Your explanation given so far is pretty good, but the attempt to explain the code (at this stage) is more complex than just showing what does and doesn't work with verbatim 6502.

Most importantly: I second thenendo's recommendation of using an emulator. FCEUX will show you stack contents in real-time, so if you've got a stack exhaustion situation it's quite easy to determine. Oh, and about FCEUX: remember, it shows the stack in "reverse order" -- that is to say, a stack that shows C5,80,A5,57,80 means that $C5 was the most-recently-pushed-value onto the stack. I'd have expected it to show the most-recently-pushed values at the end of the list, not the start.

I think you basically have a "lingering bug" somewhere in your code that manifests itself only because you're now using the stack more -- meaning this would probably bite you further down the road if you did more stack operations anyway, so fixing it now is wise.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: JMP, fine...JSR, nope...

Post by tokumaru »

thenendo wrote:Honestly what I would do is just run the rom in an emulator with a debugger like fceux or nintendulatordx, set a break-point on the routine, and step through it. You'll probably spot the problem immediately that way.
You're absolutely right. We can continue to play the guessing game here, but the best way to approach this would be to trace through the code as it runs. Set up a breakpoint at the JSR instruction, then step through the code and see if the CPU can successfully reach the subroutine and return from it, and what happens afterwards. That should give you the answer.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: JMP, fine...JSR, nope...

Post by tokumaru »

koitsu wrote:a stack that shows C5,80,A5,57,80 means that $C5 was the most-recently-pushed-value onto the stack. I'd have expected it to show the most-recently-pushed values at the end of the list, not the start.
Well, since the stack grows downwards (from $1FF down to $0100), when entries are sorted by their addresses the stack does look the way FCEUX shows it. Maybe they thought it would be confusing to display the same area of memory sorted differently (increasing addresses in the hex editor, decreasing addresses in the debug window). I guess I prefer to have the ordering consistent across windows, but I agree that there could be some sort of indication of where the top of the stack is.
I think you basically have a "lingering bug" somewhere in your code that manifests itself only because you're now using the stack more -- meaning this would probably bite you further down the road if you did more stack operations anyway, so fixing it now is wise.
That's my opinion too. The bug is probably somewhere else, and even if you can get around it by not using JSR/RTS in this particular case, it will probably manifest itself some other way later in development, so you'd better find out what's wrong now.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: JMP, fine...JSR, nope...

Post by JoeGtake2 »

Code: Select all

 The bug is probably somewhere else, and even if you can get around it by not using JSR/RTS in this particular case, it will probably manifest itself some other way later in development, so you'd better find out what's wrong now.
Yeah - that's my inclination too. I don't want to bury the gremlin and have it show up again later.
the best way to approach this would be to trace through the code as it runs. Set up a breakpoint at the JSR instruction, then step through the code and see if the CPU can successfully reach the subroutine and return from it, and what happens afterwards. That should give you the answer.
Thanks for the tip. Thus far, I have not played with the debugger in FCEUX at all. How would I determine the address of the JSR instruction in question in order to set the breakpoint? I'm looking now, but I'm not entirely sure how to find that info.

You guys rock.

Joe
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: JMP, fine...JSR, nope...

Post by tokumaru »

JoeGtake2 wrote:How would I determine the address of the JSR instruction in question in order to set the breakpoint? I'm looking now, but I'm not entirely sure how to find that info.
If your assembler can generate a listing file, that will contain all the addresses. I often do something more "hacky" though: I put "sta $ff" (or any other memory location other than $ff that's known to be unused) wherever I want to start debugging (in your case, that'd be right before the JSR) and set up a breakpoint for writes to $00ff. It's easier than looking addresses up.

When the breakpoint is triggered, just keep clicking "step into" and watch what happens as each instruction is executed. After you solve the bug, don't forget to remove the "sta $ff".
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: JMP, fine...JSR, nope...

Post by JoeGtake2 »

Great advice. I followed it. I loaded $FF to A at the start of the routine so I could see if it was getting accessed, and then step through to see what the accumulator values were and to make sure things were going where expected. I was able to follow the code line by line, and everything is exactly as expected. The position counter goes where it should (from everything I can tell), the values in the accumulators are correct at the correct times, the branching seems to work the way it is supposed to...but when playing, it does not work as supposed to (read: as it does when this code is in line rather than JSRed to).

...I still don't have any idea what the cause is, but in just doing some trial and error stuff... I tried storing the accumulator into Y before the returning from the routine (TAY), then restoring it to A after the jump back (TYA) and then did my evaluation on the value. To my surprise, this worked.

So despite the accumulator reading the right value in debugging, somehow during the return from the subroutine, the value is being corrupted. Shifting it into Y and then pulling it out of Y after the return seems to fix the problem. I find it incredibly peculiar, especially since there is no evidence of this value being corrupted in the debugger, and everything *seems* to work just as it should when stepping through it in the debugger.

Anyhow, thought I'd give an update. Weird.
Post Reply