For asm your method of breaking down tasks should be different. You don't make "generic functions that take lots of params" its not the 6502 way.
You break it down into tiny blocks that mostly take a,x,y,st.C and return a,x,y,st.C having a function that is set sprite X to {4 bytes }, by the time you push said data and restore the stack and clean up, its going to be cheaper to just inline the code that does it.
there are always exceptions and sometimes you will need to pass a large block of data to a param, but doing it on a stack of even with the variables post the call is "not the best way", typically in such cases you combine all the params into a table and pass the index. A common example that I can think of is a copy X pages of data from Y to Z, so while you might be tempted to do
Code: Select all
jsr copy
.byte pageCount
.word src,dest
..code here
it is much better to do
Code: Select all
PageCopyTable .block
numPages .byte (1,4,7)-1
src .block
_values = ($8800,$9000,$d800)
lo .byte <(_values)
hi .byte >(_values)
.bend
dest .block
src .block
_values = ($0400,$0800,$1000)
lo .byte <(_values)
hi .byte >(_values)
.bend
so rather then
Code: Select all
jsr copyBlock
.byte 1
.word $8800,$0400
you do
This way if you ever need to duplicate values you can share an index in all the locations, the detination code runs at
full speed and the code is smaller all round and you won't lose a few hours because somewhere a pla was missing and the code goes off to no mans' land.
If stack based is truly needed, one can use abs index
Code: Select all
tsx
sta@w $00fd,x ; store first param
....
sta@w $00fc,x ; second param
...
however you must make sure that nothing between the setup and the call uses more than 2 bytes of stack, or you adjust your code such that the function expects param 3/4/5/6.. bytes below the stack and then all follow the rules for that function call.
then in your code you do the same +2 for reading
Code: Select all
tsx
lda@w $00ff,x ; read first param
since the stack is not modified there is no "fix-up needed", however the called function can not use the stack otherwise it will trash parameters, which may or may not be safe by the point you do the call, and you may need to add additional room in the param set to compensate, yes this is more brittle and pain to work out than shared memory locations.
But when it comes to "modules" you tend to make a static struct somewhere, and then fill it with the data you need for the "collection" of calls, collision for example, you set up the first objects position into Collision.firstX, Collision.firstY, Collision.firstWidth, Collision.firstHeight and then call a function that iterates though the other objects which fills in the other data, then calls various functions that reference the Collision stuct. As it is shared over many calls, the loading time to the static struct is easily won back on each of the "functions" being able to load an abs address and not having to look it up each time.