• Commodore Free Magazine, Issue 81 - Part 14

    From Stephen Walsh@39:901/280 to All on Sat Jul 5 17:35:01 2014
    We're going to begin assembling a Function Library, along with a
    stack-based function-calling engine tailored to the Library. It's an "out there" type of project for sure, yet many of us have wished for one at one
    time or another in order to make things easier. I won't kid here, either - this turned out to be much harder to explain than I ever thought it would.
    Much harder. So please accept my apology now for later - when you're
    looking for a way out. Even though we won't get this all finished today,
    we will get a good start on it. We'll start today by laying out the memory usage and perceived future needs (the ability to add more functions).

    We are going to tackle this using both 6502 and 65816 assembly language,
    and we will see how to manage the parts of a modern Stack from two distinct vantage points - 6502 and 65816. We use the term "modern" in the sense
    that good examples of 6502 stack use are practically non-existent, and if available, are certainly not published in a very conspicuous place. Most
    books have done a horrible job of explaining what the 6502 Stack is and
    what it does. Yeah, I read the books, too! Invariably you're left with a
    very poor understanding of whatitis, whatever thatsit.

    Truthfully, it wasn't until I learned x86 assembly, which meant
    understanding how two different stack systems worked, that it finally all started to sink in and make some sense. I then began to wonder why we (the
    65x Community) weren't doing some of the really cool things with our stack
    that x86 programmers were doing with theirs.

    Traditionally, the 65x Stack had been used for an extremely limited list of circumstances, and rightly or not, many programmers "got by" knowing very little about how this highly efficient piece of hardware works. As I was pondering this - the "why not us too?" question - it soon became obvious:
    It was just an information gap - and that's all. The information never
    made from There to Here.

    WHICH BRINGS US TO HERE.

    Today our project has it all! - an elegant function-calling mechanism with parameter passing, return values, and local variables, or "locals." Best of all, we're going to use symbolic labels to make the concepts easier to understand and integrate into your own applications. As good assembly
    language programmers, we should always be thinking in terms of symbolic
    labels anyway. So, Note to hard-coders: Today we will be using Labels and Comments freely. Better strap yourselves in.

    - - - - - - - - - - - - - - - - - - -

    OUR PROBLEM:

    We want to write a function library and need a good way to access the
    functions within. Our solution should be something simple to operate (at
    the user's end) and easily extendable (on our end). We can assume that,
    prior to a JSR/JSL to a single address (C64 = $02A7, SCPU = $020000), the Caller pushed input parameters onto the Stack (in reverse order), loaded
    the count (# of parameters) just pushed into .A, and loaded .X/.Y with a Function ID#.

    At first glance the procedure to access a function seems fairly involved. However, if no parameters are required, the process boils down to simply loading A, X, and Y, and JSRing. Very little involvement, actually.
    Parameter passing is the elegance we talked about earlier. But more
    powerful functions often require more parameters. Our springboard will
    make parameter usage convenient for the function.

    We don't know yet what each function will do - we need a standard way to interact with the function once it is put in place. Our springboard must
    pass parameters, return results to the Caller, and process errors. We also must ensure safe return to the Caller's environment. Springboard is just fancy-talk describing a mechanism to safely transport the Caller from its
    home environment, interact within the Function environment, then travel
    safely back to the Caller's original environment.

    * First, our springboard must save any parts of the Caller's environment we intend to alter (for restoration later).

    * Next, Function Locator searches the Function Address Table, looking for
    an ID match.

    * When a match is found, the offset to the function is retrieved from bytes
    2-3 of the 4-byte function entry.

    * The address is put into a documented address with a JSR (or JSL)
    prefixed.

    * The function executes, then performs a normal RTS.

    * Our springboard restores any environment alterations, clears up the
    stack, and returns to the Caller's environment.

    Our springboard must act as a liason between the caller and function, so it needs to be easy to operate and powerful, yet fast. The way we approach
    the job depends heavily on which processor we are writing for - 6502 or
    65816.

    - - - - - - - - - - - - - - - - - - -

    COMMON WAYS OF PASSING PARAMETERS TO FUNCTIONS

    Stack: Parameters are pushed onto the Stack by the Caller, usually in
    reverse order, along with any other necessary information, before jumping
    to the springboard address. We would like for our springboard to handle
    the entire affair, including cleaning up properly afterward.

    Inline: Parameters are placed in the code stream following the call to the springboard address. The function parses the in-line params and the springboard handles fixing the Return Address (on the Stack) before the
    program resumes execution. Fast, good when memory is tight because of low memory overhead, but it mixes code and data, which can be a problem for ROM
    or segmented memory.

    Direct Page: An agreed-upon set of shared Zero/Direct Page addresses. Information is transferred using these locations. Overall the fastest
    method, it generally (but doesn't have to) relies on the presence of a
    rather large contiguous area of memory, a luxury which is not always
    available in 6502. Passing parameters in 16-bit mode is easy because
    Direct Page is relocatable.

    Register: The easiest (and most limiting) method for passing function parameters. It's just A/X/Y and SR flags, such as Carry, for BOOL data.
    With this method the function has to deal with parameters immediately
    because they are (of course) being held in volatile processor registers.

    ADDING DATA TO A STACK FRAME

    The Stack is just memory used for very specific purposes. When the
    computer first starts up the Stack Pointer will read as set to $FF (or
    $01FF for 65816). This is a pointer to the next stack address which will
    be "pushed" to. When dealing with the Stack we can calculate locations
    using relative addressing, or as offsets from a known location. The known location is always the address SP points to (which means the known location
    can and does change as data is added or removed from the Stack). It is
    this fact which causes so much confusion.

    Let's push three "fake" parameters to "dummy" and see what it does to stack alignment, assuming we began at $01FF.

    8-Bit 6502 16-Bit 65816
    PARAM1 = $0101 PARAM1 = 1
    PARAM2 = $0102 PARAM2 = 3
    PARAM3 = $0103 PARAM3 = 5

    Stack Data Placement After Pushing
    Three Parameters to "dummy"

    8-Bit 6502

    Addr SP Data Addressed As
    $01FF $FF PARAM 3 PARAM3,X / $0103,x
    $01FE $FE PARAM 2 PARAM2,X / $0102,x
    $01FD $FD PARAM 1 PARAM1,X / $0101,x
    $01FC $FC NEW SP $0100,X
    $01FB $FB ... ...
    $01FA $FA ... ...

    16-Bit 65816

    Addr SP Data Addressed As
    $01FF $01FF PARAM 3 HI 6,S (PARAM3+1)
    $01FE $01FE PARAM 3 LO 5,S (PARAM3,S)
    $01FD $01FD PARAM 2 HI 4,S (PARAM2+1)
    $01FC $01FC PARAM 2 LO 3,S (PARAM2,S)
    $01FB $01FB PARAM 1 HI 2,S (PARAM1+1)
    $01FA $01FA PARAM 1 LO 1,S (PARAM1,S)
    $01F9 $01F9 NEW SP 0,S ($01F9)
    $01F8 $01F8 ... ...
    $01F7 $01F7 ... ...

    In both cases, 6502 and 65816, we were able to turn a stack offset into a label, which means addressing data on the stack can be made easy to
    understand.

    6502



    --- MBSE BBS v1.0.01 (GNU/Linux-i386)
    * Origin: Dragon's Lair ---:- bbs.vk3heg.net -:--- (39:901/280)