JMP, fine...JSR, nope...
Moderator: Moderators
JMP, fine...JSR, nope...
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!
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!
Re: JMP, fine...JSR, nope...
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.
Just my noobish 2 cents.
Re: JMP, fine...JSR, nope...
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.
Re: JMP, fine...JSR, nope...
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?
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?
Re: JMP, fine...JSR, nope...
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.
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.
Re: JMP, fine...JSR, nope...
Pushes and pulls from the stack could mess with a subroutine, but reading from an indirect location shouldn't, no.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?
Code: Select all
lda #$FF
pha;one byte pushed to the stack
dostuff:
pla;the same byte (#$FF) is pulled off the stack
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
Re: JMP, fine...JSR, nope...
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:
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:
and then in the routine:
...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.
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:
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:
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
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.
Re: JMP, fine...JSR, nope...
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.
Is there another RTS after whatever skipDec leads to? If not, you're jumping someplace and never actually returning from the subroutine.
Re: JMP, fine...JSR, nope...
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.
Re: JMP, fine...JSR, nope...
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.
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.
Re: JMP, fine...JSR, nope...
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.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.
Re: JMP, fine...JSR, nope...
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.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.
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.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.
Re: JMP, fine...JSR, nope...
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.
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.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.
You guys rock.
Joe
Re: JMP, fine...JSR, nope...
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.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.
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".
Re: JMP, fine...JSR, nope...
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.
...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.