Structure: Best Practices? Subroutines vs. Main Program

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Structure: Best Practices? Subroutines vs. Main Program

Post by BigDumbDinosaur »

Arlet wrote:
I didn't realize you were talking about the C64.
Sorry! Doug had said he was fooling around with the WinVICE C-64 simulation. Obviously, on a less constrained machine you can be a lot more generous with zero page usage. On the 65C816 you can be even more generous, since direct page is relocatable.
BigEd wrote:
Sounds like the C64, like Acorn's Beeb, allows you to use a healthy 128 bytes or so if your program only interacts with the OS and is not planning to return to Basic.
Basically :!: that's the case. $00 and $01 on the C-64 are wired to an I/O function built into the 6510, and the "kernal" has control of everything from $90 to $FA. So a user application that doesn't interact with BASIC can use $02 to $8F, inclusive, plus $FB to $FE, inclusive.
Arlet wrote:
And if you want to return to BASIC you can probably just restart the interpreter.
Not sure how it would be done with the Beeb, but with the C-64, the BASIC ROM cold start vector at $A000 is used to get BASIC back into operation. Jumping through that vector fully initializes BASIC's zero page stuff and then finishes up by presenting the Ready prompt.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Structure: Best Practices? Subroutines vs. Main Program

Post by BigEd »

Indeed, in a similar way, on the BBC Micro a language (or application) has an entry point for a cold start. (Edit: Which is called through the OS, to allow for the existence of multiple language ROMs and the possible existence of a second processor.)
User avatar
commodorejohn
Posts: 299
Joined: 21 Jan 2016
Location: Placerville, CA
Contact:

Re: Structure: Best Practices? Subroutines vs. Main Program

Post by commodorejohn »

barrym95838 wrote:
commodorejohn wrote:
There are two main approaches you can take to saving registers. One is to have the calling routine save registers - the advantage to this technique is that the caller doesn't need to save any registers that it doesn't care about having clobbered, but the disadvantage is that that's extra code around every subroutine call.
I have a strong tendency to do it this way when I'm in 6502 land.
Interesting - I tend to go the other way, since the 6502 has few enough registers that it's usually pretty certain that a subroutine of modest complexity will need to save all of them anyway.
sark02
Posts: 241
Joined: 10 Nov 2015

Re: Structure: Best Practices? Subroutines vs. Main Program

Post by sark02 »

DougBrunelle wrote:
1. If we want to keep the .A, .X, and .Y registers in the main program safe from possible destruction by subroutines, how best to structure this?
I expect a subroutine to clobber A, X and Y unless it's an explicit feature of the subroutine to not do that. If you write a subroutine that promises not to clobber Y then, if it later evolves to do so, it's its responsibility to save/restore Y to maintain the promise.
Quote:
In some cases, I have stored the registers in memory locations- other times, I have pushed them onto the stack and popped them off the stack after subroutine calls.
Both a valid approaches. I usually save/restore A via the stack, but X and Y I'll save in memory as its cumbersome to push them via A. The exception is if the saving subroutine might be called recursively. That would be a special case and handled via the stack. These things aren't a surprise in the code I've written.
Quote:
A related question-- should it be the responsibility of the main program to save the registers, or should it be done by each subroutine? Seems to me that we should be able to jsr to any subroutine and assume that our registers will not be changed upon return from the subroutine.
Different CPUs have different calling conventions. In some CPUs there are caller-saved (ones you can expect a subroutine to clobber) vs. callee-saved (ones that will retain their value). With only three registers, I'd rather not make it a rule.
Quote:
2. How best to implement other variables? If, for example, I have a main program which uses a zero-page pointer "ScrPtr" at $fb, should the main program be the only code block which manipulates ScrPtr, or is it common practice for subroutines to work with this kind of variable (global)?
Global variables are common in 6502 programs as trying to be overly "structured programming" about it leads to very slow and cumbersome code. That doesn't mean it's a free for all... Rather there should be variables that are public and variables that are private. You can use variable names to convey this, or #include files that only export certain names. Whatever you do, it should not be ambiguous.
Quote:
I have heard that, ideally, our Main code block should be just a list of subroutine calls.
If you ask three people you'll get four different answers. It's advisable if you're just starting out to err on the structured side so that you can keep track of everything and not get lost if you have to come back to a program a week, month or year later. With more experience you'll develop your own preferences, and will be able to let go of overly rigid structures in order to gain speed or reduce code size; all whilst maintaining a supportable level of structure.
DougBrunelle
Posts: 4
Joined: 04 May 2015

Re: Structure: Best Practices? Subroutines vs. Main Program

Post by DougBrunelle »

Thank you all for your comments and suggestions! Thought-provoking and excellent info. Btw I'm reading through Jim Butterfield's "Machine Language Programming for the Commodore 64 and Other Commodore Computers". Jim was "The Man" back in the '80s, as I recall. RIP, he passed on in 2007..

--db
Post Reply