Warning: wall of text.
In some previous threads (here and here) me, Miau and Disch have been talking about macros, how to use the stack in a better way, parameters etc.
Disch came out with the concept of a software stack in zero page instead of using the real one. After thinking a little bit about it, I decided to make one and tried to make a few macro to help the process of using it.
Here's the concept. First, you need to define a variable in zero page for the stack pointer and some constant for the location of the stack. You have to init it like the real stack with the value $FF
The init must be done after your init your memory to zero. Then, you need to create a method that defines a few parameters. The way you define a method is this way:
The structure defined inside the .proc is important: it defines the size of your parameters and where they are located. It will become clearer later.
Now you need a few macro to make your life easier (and make your code cleaner to read). One is for allocating memory, one is for loading parameters and the other one is for releasing memory.
Here's the macros:
Now that we have some macro to do the job, it's time to see how to use them. Here's the code example to call a function with parameters:
Now without explanation it will be hard to understand. The first line allocate ram on the stack. How it work is that is the macro receive as a parameter the function parameter's structure (subShowAnimFrame::Param). The macro will check the size of the structure and will allocate the ram right away. Then it will set the X register with the current stack location.
The spos puts a parameter on the stack. The first parameter is the location on the stack (subShowAnimFrame::Param::PosX). The second one is the value you want to put. In it's current stage, you can only put 8 bits value. I want to improve it later.
One thing you will find interesting is the order of parameters is not important. Since the structure knows the location in memory of a parameter and you allocated the RAM you need before using it, you don't have to worry about parameters order. this will be the same thing when retrieving them too.
Now that we called the method, we need to know how to access those parameters inside it. Here's the code example:
Since you use a software stack in zero page, you must first load the X register with the stack pointer. Then you can access the location of the variable with stack location + location in RAM (SOFTWARE_STACK + Param::SpriteDir). You don't have to worry if it's parameter 1 or 2, the structure will take care of it.
The pro of this approach is to have dynamic allocation of parameters. Once you understand how to use the macro properly, it's quite simple. It removes the possibility of using by accident some temp variable defined elsewhere too. If you want local variables, you could just add a few extra parameters inside the struct. The value of those "parameters" will be initialized inside the method. This way, the memory for the parameters and local variables would be allocated before calling the function.
The cons are that there will be a small price in performance. You need to load the stack pointer in X every time you want to access a value. Compared to a direct zero page variable, you have do do a few processing before being able to access it. So I will not recommend this approach for critical method used in the vblank for example. But in some other cases, it could make the programming easier for methods that have many parameters. You don't have to worry how many temp variable you have and if someone is using them.
I have been testing it yesterday and for now it's working. I need to test it more to see if there is any issues I didn't see yet.
That's was a long message. Any comment on the subject will be appreciated.
In some previous threads (here and here) me, Miau and Disch have been talking about macros, how to use the stack in a better way, parameters etc.
Disch came out with the concept of a software stack in zero page instead of using the real one. After thinking a little bit about it, I decided to make one and tried to make a few macro to help the process of using it.
Here's the concept. First, you need to define a variable in zero page for the stack pointer and some constant for the location of the stack. You have to init it like the real stack with the value $FF
Code:
.segment "ZEROPAGE"
zpStackPointer: .res 1 ; Software stack pointer
; ... Some code later
; Define for stack location
SOFTWARE_STACK = $00 ; Software stack pointer is in zero page
.segment "CODE"
ldx #$FF
stx zpStackPointer ; Set software stack pointer
zpStackPointer: .res 1 ; Software stack pointer
; ... Some code later
; Define for stack location
SOFTWARE_STACK = $00 ; Software stack pointer is in zero page
.segment "CODE"
ldx #$FF
stx zpStackPointer ; Set software stack pointer
The init must be done after your init your memory to zero. Then, you need to create a method that defines a few parameters. The way you define a method is this way:
Code:
; The procedure with parameter
.proc subShowAnimFrame
; Parameters on software stack:
.struct Param
PosX .byte ; X location
PosY .byte ; Y Location
SpriteDir .byte ; Sprite direction
.endstruct
; ... some code here
.endproc
.proc subShowAnimFrame
; Parameters on software stack:
.struct Param
PosX .byte ; X location
PosY .byte ; Y Location
SpriteDir .byte ; Sprite direction
.endstruct
; ... some code here
.endproc
The structure defined inside the .proc is important: it defines the size of your parameters and where they are located. It will become clearer later.
Now you need a few macro to make your life easier (and make your code cleaner to read). One is for allocating memory, one is for loading parameters and the other one is for releasing memory.
Here's the macros:
Code:
;->BEGIN----------------------------------------------------------------------
; Allocate memory on the stack for parameters
;
; Parameter:
; paramStruct : The structure that represent the parameters that needs memory
; to be allocated.
;
; Notes: The stack pointer value is put by default in the X index at the end. This
; is because we expect to store parameters after alocating memory.
;
.macro alocateMemOnStack paramStruct
lda zpStackPointer ; Load stack pointer
sec
sbc #.sizeof(paramStruct) ; Adjust stack pointer
sta zpStackPointer ; save it
tax ; Store stack pointer value in index
.endmacro
;-<END------------------------------------------------------------------------
;->BEGIN----------------------------------------------------------------------
; Release the memory stored for the parameters on the stack
;
; Parameter:
; paramStruct : The structure that represent the parameters that needs memory
; to be released.
;
.macro releaseMemFromStack paramStruct
lda zpStackPointer ; Load stack pointer
clc
adc #.sizeof(paramStruct) ; Adjust stack pointer
sta zpStackPointer ; save it
.endmacro
;-<END------------------------------------------------------------------------
;->BEGIN----------------------------------------------------------------------
; Store parameter on the stack
;
; Parameter:
; paramLocation : Where in memory relative to the stack pointer that is should
; be stored.
; value : The value we want to store
;
; Note: We should make it more inteligent and knows when it's a 16/8 value to store
;
.macro spos paramLocation, value
lda value
sta SOFTWARE_STACK + paramLocation, x
.endmacro
;-<END------------------------------------------------------------------------
; Allocate memory on the stack for parameters
;
; Parameter:
; paramStruct : The structure that represent the parameters that needs memory
; to be allocated.
;
; Notes: The stack pointer value is put by default in the X index at the end. This
; is because we expect to store parameters after alocating memory.
;
.macro alocateMemOnStack paramStruct
lda zpStackPointer ; Load stack pointer
sec
sbc #.sizeof(paramStruct) ; Adjust stack pointer
sta zpStackPointer ; save it
tax ; Store stack pointer value in index
.endmacro
;-<END------------------------------------------------------------------------
;->BEGIN----------------------------------------------------------------------
; Release the memory stored for the parameters on the stack
;
; Parameter:
; paramStruct : The structure that represent the parameters that needs memory
; to be released.
;
.macro releaseMemFromStack paramStruct
lda zpStackPointer ; Load stack pointer
clc
adc #.sizeof(paramStruct) ; Adjust stack pointer
sta zpStackPointer ; save it
.endmacro
;-<END------------------------------------------------------------------------
;->BEGIN----------------------------------------------------------------------
; Store parameter on the stack
;
; Parameter:
; paramLocation : Where in memory relative to the stack pointer that is should
; be stored.
; value : The value we want to store
;
; Note: We should make it more inteligent and knows when it's a 16/8 value to store
;
.macro spos paramLocation, value
lda value
sta SOFTWARE_STACK + paramLocation, x
.endmacro
;-<END------------------------------------------------------------------------
Now that we have some macro to do the job, it's time to see how to use them. Here's the code example to call a function with parameters:
Code:
; Alocate ram the size of all parameters
alocateMemOnStack subShowAnimFrame::Param
; Store parameters on stack
spos subShowAnimFrame::Param::PosX, #$00
spos subShowAnimFrame::Param::SpriteDir, zpDebugMMDir
spos subShowAnimFrame::Param::PosY, #$64
jsr subShowAnimFrame
; Release it
releaseMemFromStack subShowAnimFrame::Param
alocateMemOnStack subShowAnimFrame::Param
; Store parameters on stack
spos subShowAnimFrame::Param::PosX, #$00
spos subShowAnimFrame::Param::SpriteDir, zpDebugMMDir
spos subShowAnimFrame::Param::PosY, #$64
jsr subShowAnimFrame
; Release it
releaseMemFromStack subShowAnimFrame::Param
Now without explanation it will be hard to understand. The first line allocate ram on the stack. How it work is that is the macro receive as a parameter the function parameter's structure (subShowAnimFrame::Param). The macro will check the size of the structure and will allocate the ram right away. Then it will set the X register with the current stack location.
The spos puts a parameter on the stack. The first parameter is the location on the stack (subShowAnimFrame::Param::PosX). The second one is the value you want to put. In it's current stage, you can only put 8 bits value. I want to improve it later.
One thing you will find interesting is the order of parameters is not important. Since the structure knows the location in memory of a parameter and you allocated the RAM you need before using it, you don't have to worry about parameters order. this will be the same thing when retrieving them too.
Now that we called the method, we need to know how to access those parameters inside it. Here's the code example:
Code:
ldx zpStackPointer
lda SOFTWARE_STACK + Param::SpriteDir, x ; Check sprite orientation.
lda SOFTWARE_STACK + Param::SpriteDir, x ; Check sprite orientation.
Since you use a software stack in zero page, you must first load the X register with the stack pointer. Then you can access the location of the variable with stack location + location in RAM (SOFTWARE_STACK + Param::SpriteDir). You don't have to worry if it's parameter 1 or 2, the structure will take care of it.
The pro of this approach is to have dynamic allocation of parameters. Once you understand how to use the macro properly, it's quite simple. It removes the possibility of using by accident some temp variable defined elsewhere too. If you want local variables, you could just add a few extra parameters inside the struct. The value of those "parameters" will be initialized inside the method. This way, the memory for the parameters and local variables would be allocated before calling the function.
The cons are that there will be a small price in performance. You need to load the stack pointer in X every time you want to access a value. Compared to a direct zero page variable, you have do do a few processing before being able to access it. So I will not recommend this approach for critical method used in the vblank for example. But in some other cases, it could make the programming easier for methods that have many parameters. You don't have to worry how many temp variable you have and if someone is using them.
I have been testing it yesterday and for now it's working. I need to test it more to see if there is any issues I didn't see yet.
That's was a long message. Any comment on the subject will be appreciated.