I've been wanting to create a function framework for ASM6 for a little while now, both so I could better understand the way programming languages like C work at the low level, and also use C-like functions inside my assembly programs

I did a bit of research, did some experimentation.. and ended up with a pretty decent collection of macros for function calls in ASM6! It seems to work pretty well, but any optimizations or bug fixes are appreciated

The values being passed are kept in a stack at $0500 - $05FF which is separate from the internal stack. This stack does not contain program pointers, just data and pointers to data. Program pointers are still handled using the internal stack. The separation of data and internal stacks make code less error prone and also increases the level of nesting possible.

In addition to passing parameters, you can allocate space in the stack for temporary local variables. This is much more flexible than trying to reuse a handful of global variables as reusable temporary variables.
http://supermariounlimited.com/wiki/C_Style_Functions

and here's a test case.. nothing exciting, just writes some values to memory. You can check out the contents of the stack at $0500 as well to see that everything is working as expected

Wouldn't it make more sense to keep the stack pointer in x at all times? Then code could access stack variables via $500-i,x, where i is the index of the local. For example, if it has three locals, it accesses them as $500,x $4FF,x and $4FE,x. That makes them much more convenient and efficient to use.

I always tough the concept of using a parameter/local variable stack in assembly is very interesting. After all I have to admit most of the errors I had when writing my game engine in assmebly were that I had problem with my variables. Using a solid system would have spared me hours of annoying bug track.

I've had a system where I can use 4 "temp" variables plus some others semi-temp variables, both to store temporary data and to pass parameters, but the routines calling themselves have to agree which one is using which temp variables. If more varaibles are needed then I create actual variables for this specific purpose.

However, the resulting code is slower, and acessing the stack gets too slow it could become really limitating to work with such a system. Some improbement should be done to make this faster I guess.

_________________Life is complex: it has both real and imaginary components.

Even with your approach, you can't use X whenever you're accesing locals. The main benefit of being able to use X freely is speed, but allowing that slows access to stack variables.

if you want you can load x with the location of the first local var. then if you don't touch X it will work like you're talking about (i think) but i wouldn't want to make that a rigid requirement.. if you call another function you would have to push x onto the stack anyways. i can see the advantage to making sure X set on function load though

Quote:

If $100 bytes were enough for both stacks, you could just use the normal stack. That'd be faster.

yeah probably, but then you'd have to implement a whole different function system

the point of this collection of macros isn't to create the most efficient code possible, but rather to make developing easy as possible. it's also kind of a "proof of concept"... though eventually I would like to create some kind of higher level language which is converted into assembly and compiled with asm6

there's no reason why i did anything any particular way except that is the way that made sense to me. I could make the stack work "backwards" and perhaps if i had dealt with stacks often that would a more intuitive way of working with it.

the point of this collection of macros isn't to create the most efficient code possible, but rather to make developing easy as possible. it's also kind of a "proof of concept"... though eventually I would like to create some kind of higher level language which is converted into assembly and compiled with asm6

hey blargg i thought about what you said and I at least managed to make it so most macros expect X to be the same as stackPtr, saving a lot of ldx stackPtr. if x is overwritten, you can just load it from stackPtr again

stackPtr always points to a value in the stack. That value is a pointer to the start of the local vars

I'm gonna try to make it so Y is always the pointer to the start of local vars

edit: well i got Y to always be the ptr to local vars, but now i have to think of a new way to return values lol

I updated the code significantly to improve efficiency. Register X is kept as the stack pointer and Register Y is the pointer to the Local variables. ldaLocal uses $500+i,x instead of adc and the other stuff.

I'm keeping the code on my wiki for now so I don't have to paste the code into the message board over and over

Something that always points to the local variables for the current function is usually called the [stack] frame pointer. But it's unnecessary, as you can just adjust the stack as necessary and access locals off the stack pointer. Also, it seems much better to leave Y free and have X be reserved for something, because the (zp),y addressing mode is the most important to have available to user code all the time, rather than (zp,x).

you can access variables from the stack pointer, but if you allocate more space on the stack, then the relative position of a given variable will change. thats why i'm keeping track of both pointers in the registers.

i'm also keeping copies of the pointers in ZP so i can restore their values easily if either register is needed for other operations. I only really NEED to keep track of the stack ptr since i can the frameptr from the end of the stack.. but it's faster to pull it from ZP obviously

i even added optional parameters to functions which expect registers to be set so that the registers can be reset if need be. register Y is now only used when accessing local variables using macros.

but you could access them relative to the stack pointer if a) needed to use Y and couldn't restore it during a specific operation and b) you knew the position of the value relative to the stack pointer.

it seems like it's pretty flexible.. the only thing I don't like is having to remember to restore X/Y (less coding friendly) but it does save some lines of code. I could make it so the macros default to reloading the registers from the ZP values (more coding friendly but less efficient) but it's less common that they need to be reloaded

i'm working on a "fastcall" for simple functions which just use the registers too

I just have a random trought, I don't know how usefull it can be.
If stack pointer is at $ff and the stack grows at $500, why not always store #$05 into $fe so that you can acess the stack 2 ways, the most optimal is choosen in function of what we want :

ldx StackPointer
lda $500,X

or

ldy #$00 (or whathever other value)
lda ($fe),Y

_________________Life is complex: it has both real and imaginary components.

Who is online

Users browsing this forum: No registered users and 11 guests

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