6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 12:54 am

All times are UTC




Post new topic Reply to topic  [ 12 posts ] 
Author Message
PostPosted: Thu Feb 28, 2013 6:50 pm 
Offline

Joined: Mon Jan 07, 2013 2:42 pm
Posts: 576
Location: Just outside Berlin, Germany
I was wondering if there is any convention or any recommendations for best practices about what to do when, say, X and Y are changed during a subroutine call. Is the subroutine expected to hand the original values back, even when it needs these registers, or is it assumed that the caller will put them in a safe spot before the call? AFAIK, the ARM convention is that a subroutine has to make sure the values that were passed on are protected and passed back. However, what I've seen from other people's code is that there is not such recommendation on the 65-family.

As I'm just starting out to write what could be lots of code, I thought I might start out the "best" way from the ground up, if there is one. Leventhal is silent on this.

(Actually, since I'm aiming for a 65c02, this is not much of a problem, because the subroutine can just phx and phy at the beginning and ply and plx at the end. Just want to know if there is a preferred way of doing this.)

Thanks!


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 28, 2013 8:15 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
Frequently X and Y are used to pass inputs and outputs too; so if they were preserved, it would mean the routine didn't do anything.

I don't think one can make any universal recommendations. In pure assembly, I usually have no reason to preserve them; but in Forth, X is used as the data stack pointer (like S is used as the return stack pointer), so it each primitive (ie, Forth words defined in assembly) must end with X pointing to the top of the data stack (although internally it can use X for something else in the few times that that is needed).

It seems like the books always go overboard in recommending saving things, because the writer doesn't know your needs and doesn't want to cause problems, so he wants to be on the safe side. This leads to a lot of overhead though. It's especially visible in how they recommend doing interrupt-service routines (ISRs), making them much slower than they need to be. It goes along with polling for every interrupt source in every I/O IC in the computer, even though it's a waste of time polling sources that are not enabled (meaning most of them).

I would say just make it clear in the comments what each routine's input requirements are and where the output is put and what registers it affects unless it's super obvious already. There may be cases where you will need to push and pull X for example in a host routine that uses it as a loop counter if there's a subroutine in the loop that will mess it up; and that seems to be more efficient than having the subroutine save it every time and slow things down for other places it is called that don't care what it does with X. Still, with good planning, there's almost no conflict. Like BigEd put it so well, "With 6502, I suspect more than one beginner has wondered why they can't do arithmetic or logic operations on X or Y, or struggled to remember which addressing modes use which of the two. And then the intermediate 6502 programmer will be loading and saving X and Y while the expert always seems to have the right values already in place."

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 28, 2013 9:20 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
scotws wrote:
the ARM convention is that a subroutine has to make sure the values that were passed on are protected and passed back.


ARM has 16 registers. Some are used for parameter passing (r0-r3) and those can be clobbered by the called routine. Most of the other registers must be saved. However, that system works pretty well on ARM because of the large number of registers and widespread use of higher level languages such as C/C++ where interoperability between diverse pieces of code is useful. On the 6502, with its limited number of registers, and mostly assembly programming, a fixed rule would likely be too restrictive.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2013 1:31 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
For 6502, I would imagine you would have such conventions across major boundaries (such as OS calls) but within an application or the OS the contract would be ad-hoc. Most leaf routines will be free to use both X and Y and many will need to. If a non-leaf makes use of X or Y it would be more likely to act as a constraint on its child routines than to save and reload. But I am guessing. It would be interesting to take something like BBC BASIC which was rewritten for 65C02 and see how many PLX/PLY are used, and if any fewer LDX/LDY are present.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2013 5:13 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
Then there's the Atari language Action! It didn't have a calling convention. Rather, when each function was compiled, the local parameters were statically located in RAM. Populate the values, call the routine.

Since many consider the 6502 Zero Page effectively a register array, you can easily see allocating some fixed number, say, 8 bytes (4 words) as registers able to be scheduled for parameter passing, with the rest stuffed on a stack somewhere. Or simply do the best of both worlds like Forth does and use a chunk of Zero Page as a stack, and pass them there.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2013 5:27 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
whartung wrote:
Since many consider the 6502 Zero Page effectively a register array, you can easily see allocating some fixed number, say, 8 bytes (4 words) as registers able to be scheduled for parameter passing, with the rest stuffed on a stack somewhere. Or simply do the best of both worlds like Forth does and use a chunk of Zero Page as a stack, and pass them there.

Actually, besides the data stack, Forth also puts in zero page an array of about 8 bytes often called just "N" which is for primitives to use as internal scratchpad area that's more efficient than the indexed accesses to the data stack. The values in those bytes at the beginning and end of each primitive are irrelevant; IOW, they are never used to pass parameters between routines.

I guess a tutorial on stacks is something I should add (unless someone beats me to it).

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2013 5:59 am 
Offline

Joined: Mon Jan 07, 2013 2:42 pm
Posts: 576
Location: Just outside Berlin, Germany
GARTHWILSON wrote:
I guess a tutorial on stacks is something I should add (unless someone beats me to it).


I'd very much appreciate that!


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2013 4:05 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Here are the relevant percentages of X and Y related opcodes, for BBC BASIC4 on 65C02 versus BBC BASIC2 on 6502
Code:
19.7  19.3  LDY
14.1  11.5  LDX
11.1  12.9  INY
 8.6  10.4  STY
 7.0   7.8  STX
 5.3   6.7  DEY
 4.8   4.3  TYA
 4.8   5.2  CPX
 4.0   3.5  TAX
 3.7   4.4  TAY
 3.3   3.7  CPY
 3.1   2.7  INX
 2.8   3.3  DEX
 2.6   2.9  TXA
 1.3        PHX
 1.1        PLX
 1.0        PLY
 0.8        PHY
 0.6   0.7  TXS
 0.6   0.6  TSX

Basic 4 disassembly from http://www.8bs.com/basic/basic4.htm
Rough and ready Basic 2 disassembly using run6502.
These are static percentages as they'd appear in the source, not the dynamic percentages according to how often they are executed.

Edit: here are stats from Woz' integer BASIC for Apple 1:
Code:
24.1  LDY
12.1  STY
11.8  INY
7.4   LDX
7.4   INX
7.4   DEY
6.2   TAY
4.0   TYA
4.0   STX
3.7   TAX
3.4   DEX
2.8   TXA
2.5   CPY
1.5   CPX
0.9   TXS
0.3   TSX

which may say something about different people's coding styles, or possibly different levels of experience.
(Using ftp://ftp.apple.asimov.net/pub/apple_II ... sembly.txt)


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 02, 2013 9:37 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
scotws wrote:
GARTHWILSON wrote:
I guess a tutorial on stacks is something I should add (unless someone beats me to it).

I'd very much appreciate that!

Ok, I'm taking suggestions of things to address, from beginners and experts alike. Hopefully I'll even learn something from what people PM or email me, and hopefully each good idea will lead to more. I won't be getting on it immediately, but I can be doing some mental organization and making notes as things come to mind or as forum members suggest them. I can imagine quite a few aspects and uses and a few tricks to discuss. The 6502 primer was written partly to address misunderstandings that showed in newcomers' forum posts over and over.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 02, 2015 6:47 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
scotws wrote:
GARTHWILSON wrote:
I guess a tutorial on stacks is something I should add (unless someone beats me to it).


I'd very much appreciate that!

It's up, at http://wilsonminesco.com/stacks/ .

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 02, 2015 7:12 am 
Offline

Joined: Mon Jan 07, 2013 2:42 pm
Posts: 576
Location: Just outside Berlin, Germany
Wee, great!


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 02, 2015 8:27 am 
Offline

Joined: Mon Jan 26, 2015 6:19 am
Posts: 85
scotws wrote:
I was wondering if there is any convention or any recommendations for best practices about what to do when, say, X and Y are changed during a subroutine call. Is the subroutine expected to hand the original values back, even when it needs these registers, or is it assumed that the caller will put them in a safe spot before the call?

For a processor with few registers like the '02, the best convention is probably that if the caller expects a register to be preserved across a subroutine call then it should take the responsibility of saving the register beforehand and restoring it afterwards. Otherwise, every subroutine would be spending time saving and restoring registers whether is was needed or not (imagine what that would be like when you have deeply nested subroutines).

Its a similar scenario (and for the same reason) in FORTH. Each word consumes the parameters passed to it on the stack (and returns its own computations on the stack). So if you wish to save any data on the stack before running the word, you must DUPlicate it first.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: