Jump to content
IGNORED

Assembler clear return stack / return info


Recommended Posts

It seems maybe my program sometimes can get lost in subroutines or some misdirected BEQ BNE or BCC BCS linked to RTS with may work if the programmer know where that RTS will return to and it flows naturally in a loop that way.. The RTS will return to the previous JSR, isn that right ?

 

Anyway, is there a easy way of clearing the Return info, its on the stack ? So that all Return adresses are clear , So that the programmer can get control again of the Adresses in the subrotuine. I know that

RTS is normally linked to JSR.

 

I should know this by now, but it seems my way of learning assembler is kinda unorganized hehe.

 

 

 

 

Link to comment
Share on other sites

Thank you, I didn,t mean to read the adress, But to clear the adress on the stack, as in clear memory, remove the return adress completely,

Or to put it this way, I want my program to behave like there never was given a JSR, ofcoarse is only to be used at a certain place in the program.

 

is it as easy as a CLC command ?

Edited by Grevle
Link to comment
Share on other sites

It's not hard, but it's probably a bad idea. The reason you are getting lost in your return stack is your own fault, and you should fix your code rather than arbitrarily deciding to clear the stack. If you 'clear' the stack, you will be destroying return information, and since you don't know where to stop, you will probably trash the whole machine at some point.

 

Every JSR must be followed by an accompanying RTS, including the JSR that called you in the first place. Branches, such as JMP, BNE, etc., do not affect the stack, but of course they can send you somewhere incorrect unless your code is correct. There are certain tricks to be played with pushin and pulling RTS values to and from the stack, but it doesn't sound like you'd be up to that, so I'd follow the rules. Interrupts also use the stack for similar reasons, and the RTS equivalent is RTI.

 

It *really* helps to have a trace mechanism, I'd recommend you to use the Altirra emulator and it's absolutely fantastic debugger.

 

*edit* sorry hadn't read your post above while I was editing.

Edited by danwinslow
Link to comment
Share on other sites

 

 

 

 

It *really* helps to have a trace mechanism, I'd recommend you to use the Altirra emulator and it's absolutely fantastic debugger.

 

 

 

Thanks, How to trace my assembler program ? im not so familiar with the Altirra debugger. Or is there some other way of tracing the assembler program ? other way than using Altirra ?

Edited by Grevle
Link to comment
Share on other sites

I'm sure there are other native ATARI ways, but basically you download and setup the Altirra emulator on a PC, and use that. You'll have to get your code over there, and that's the hard part, but it can mount any ATR or pc directory as an 'atari disk'. So you'd need some way to turn your atari disk that you're using into an ATR, or maybe you are already using some kind of device other than an actual atari disk drive, and maybe already have it as an ATR.

But anyway, then in Altirra you turn the debugger on, hit F8 (break) and use console commands like BP $1234 to set a breakpoint. Then when your breakpoint hits it will stop and you will see a nice disassembled panel full of your code with all registers shown. You can single step and stuff like that. It makes assembler programming almost easy. Almost.

Link to comment
Share on other sites

Use Altirra's History window. It groups executed instructions by nesting level, so if you have a crash resulting from mismatched JSR+RTS, it'll show you pretty quickly where that happened.

 

The stack only holds bytes and doesn't track whether bytes are pushed data or JSR return addresses. If you are also saving bytes on the stack with PHA/PHP/PLA/PLP, those need to be matched too. RTS can't magically find the next JSR return address on the stack -- it just pulls off the next two bytes and assumes they're a valid return address. The 6502 also has no notion of an "empty" stack. If you try to do RTS without a JSR, the 6502 will simply pull two random garbage bytes and use those as the return address (and then crash).

 

As mentioned earlier, you can "skip" an RTS by using PLA to remove bytes from the stack without doing a return. You need one PLA for each unmatched PHA/PHP and two PLAs for each unmatched JSR. This can also be done by saving and restoring the stack pointer with TSX and TXS. This is an advanced technique typically used for handling errors.

  • Like 1
Link to comment
Share on other sites

Every JSR must be followed by an accompanying RTS, including the JSR that called you in the first place.

 

I consider this recommendation as too far-reaching. Sure, this is the "good school" of coding, yet it may happen that the place you want to return to no longer exists (self-modyfing code, overwriting memory with next program part, etc.).

 

In one of my games I JSR-ed into the subroutine which, based on the game state, internally decided whether the calling code should be overwritten. In such case I had to call "PLA PLA" to skip one "layer" of RTS and return directly to the caller's caller.

 

So, your advice is very solid, but I wouldn't say "must be followed", since some less experienced developers may think that there are no exceptions.

_________

Edit: Just now I see that you mentioned "certain tricks to be played with stack". I'll however keep my post as is as a possible example of such trick :)

Edited by mgr_inz_rafal
Link to comment
Share on other sites

In one of my games I JSR-ed into the subroutine which, based on the game state, internally decided whether the calling code should be overwritten. In such case I had to call "PLA PLA" to skip one "layer" of RTS and return directly to the caller's caller.

Or you could decide before JSRing and do JMP ;)

  • Like 1
Link to comment
Share on other sites

It can lead to confusion if you play around with the stack for the sake of aborting subroutines etc.

 

TSX gives you the SP in X register so easy enough to know where the next byte will be pushed to. Not good practice to assume you'll be at some absolute location though, you could have a different stack environment depending on how your program was loaded and executed.

 

But yes... for the purpose of aborting a subroutine, two PLAs is probably the best method.
It's also sort of "standard practice" in that you can call functions by maintaining a table of entry points decremented by 1, then push the address to the stack and do an RTS to perform the call.

Link to comment
Share on other sites

don't mess with stack.... Altirra debugger shows clearly where the mess starts... and you can setup breakpoint right before the JSR and then single step through or step over and see what interferred with the stack... fex. in my case I often had NMI coming into way (with my own NMI routine) or some code messing with stack.

  • Like 1
Link to comment
Share on other sites

In the modern day with emulation and advanced debug methods there's much less need for set breakpoints and some of the older methods.

Much better is conditional stops. Just something like a break based on an access to an address range can be setup in seconds and save hours of headscratching.

Edited by Rybags
  • Like 1
Link to comment
Share on other sites

One encountering an RTS, the 6502 will pull the return address (LSB first), add 1 to it, and resume execution at the resulting address. JSR transparently handles pushing of the return address, but you can create a pseudo jump table by pushing the address yourself:

	lda #> [Address-1]
	pha
	lda #< [Address-1]
	pha
	rts

Of course to make this useful, you would derive "Address" from an indexed table.

Edited by flashjazzcat
Link to comment
Share on other sites

 

I consider this recommendation as too far-reaching. Sure, this is the "good school" of coding, yet it may happen that the place you want to return to no longer exists (self-modyfing code, overwriting memory with next program part, etc.).

 

In one of my games I JSR-ed into the subroutine which, based on the game state, internally decided whether the calling code should be overwritten. In such case I had to call "PLA PLA" to skip one "layer" of RTS and return directly to the caller's caller.

 

So, your advice is very solid, but I wouldn't say "must be followed", since some less experienced developers may think that there are no exceptions.

_________

Edit: Just now I see that you mentioned "certain tricks to be played with stack". I'll however keep my post as is as a possible example of such trick :)

 

Oh absolutely, I was just saying if you aren't sure whats going on, it's best to follow the 'strict rules'.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...