zp scope and parameter management with ca65 macros.

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
GradualGames
Posts: 1106
Joined: Sun Nov 09, 2008 9:18 pm
Location: Pennsylvania, USA
Contact:

zp scope and parameter management with ca65 macros.

Post by GradualGames »

I thought I'd share some more macro stuff I've been doing. I continue to use some of zp for parameters and scratch space. I came up with some macros which basically assign equates to a series of byte or word locations in zp so I can have "local variables" inside of routines. I have a corresponding macro which copies parameters into zp.

Code: Select all

;This macro can be used to automatically enumerate local equates within a routine
;to line up with bytes or words in zp. ZP prefix should be "w" or b" so that the
;symbols passed in can be associated with b0, b1, b2 etc. or w0, w1, w2, etc.
;Any time inner routines also use this macro, care should be taken to leave space
;for the inner routine's zp scope by leaving the first n parameters blank.
.macro zp_equates zp_prefix,\
                  equate0,\
                  equate1,\
                  equate2,\
                  equate3,\
                  equate4,\
                  equate5,\
                  equate6,\
                  equate7,\
                  equate8,\
                  equate9,\
                  equate10,\
                  equate11,\
                  equate12,\
                  equate13,\
                  equate14,\
                  equate15,\
                  equate16,\
                  equate17,\
                  equate18,\
                  equate19

    .ifnblank equate0
        equate0 = .ident(.concat(.string(zp_prefix), "0"))
    .endif
    .ifnblank equate1
        equate1 = .ident(.concat(.string(zp_prefix), "1"))
    .endif
    .ifnblank equate2
        equate2 = .ident(.concat(.string(zp_prefix), "2"))
    .endif
    .ifnblank equate3
        equate3 = .ident(.concat(.string(zp_prefix), "3"))
    .endif
    .ifnblank equate4
        equate4 = .ident(.concat(.string(zp_prefix), "4"))
    .endif
    .ifnblank equate5
        equate5 = .ident(.concat(.string(zp_prefix), "5"))
    .endif
    .ifnblank equate6
        equate6 = .ident(.concat(.string(zp_prefix), "6"))
    .endif
    .ifnblank equate7
        equate7 = .ident(.concat(.string(zp_prefix), "7"))
    .endif
    .ifnblank equate8
        equate8 = .ident(.concat(.string(zp_prefix), "8"))
    .endif
    .ifnblank equate9
        equate9 = .ident(.concat(.string(zp_prefix), "9"))
    .endif
    .ifnblank equate10
        equate10 = .ident(.concat(.string(zp_prefix), "10"))
    .endif
    .ifnblank equate11
        equate11 = .ident(.concat(.string(zp_prefix), "11"))
    .endif
    .ifnblank equate12
        equate12 = .ident(.concat(.string(zp_prefix), "12"))
    .endif
    .ifnblank equate13
        equate13 = .ident(.concat(.string(zp_prefix), "13"))
    .endif
    .ifnblank equate14
        equate14 = .ident(.concat(.string(zp_prefix), "14"))
    .endif
    .ifnblank equate15
        equate15 = .ident(.concat(.string(zp_prefix), "15"))
    .endif
    .ifnblank equate16
        equate16 = .ident(.concat(.string(zp_prefix), "16"))
    .endif
    .ifnblank equate17
        equate17 = .ident(.concat(.string(zp_prefix), "17"))
    .endif
    .ifnblank equate18
        equate18 = .ident(.concat(.string(zp_prefix), "18"))
    .endif
    .ifnblank equate19
        equate19 = .ident(.concat(.string(zp_prefix), "19"))
    .endif

.endmacro

;This macro can be used to copy byte or word zp parameters from elsewhere in zp
;to the bottom of the b0, b1, b2 or w0, w1, w2 etc. zp workspace area. The move8
;and move16 macros are invoked which in turn invoke ins_lo and ins_hi, which examine
;the names of the symbols and allow contiguous zp variables to be used as well as
;structures-of-arrays whose symbols begin with "entity_" and end with "_lo" or "_hi."
.macro zp_params zp_prefix,\
                 ident0,\
                 ident1,\
                 ident2,\
                 ident3,\
                 ident4,\
                 ident5,\
                 ident6,\
                 ident7,\
                 ident8,\
                 ident9,\
                 ident10,\
                 ident11,\
                 ident12,\
                 ident13,\
                 ident14,\
                 ident15,\
                 ident16,\
                 ident17,\
                 ident18,\
                 ident19

    .ifnblank ident0
        .if .xmatch({zp_prefix}, b)
            move8 ident0, .ident(.concat(.string(zp_prefix), "0"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident0, .ident(.concat(.string(zp_prefix), "0"))
        .endif
    .endif
    .ifnblank ident1
        .if .xmatch({zp_prefix}, b)
            move8 ident1, .ident(.concat(.string(zp_prefix), "1"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident1, .ident(.concat(.string(zp_prefix), "1"))
        .endif
    .endif
    .ifnblank ident2
        .if .xmatch({zp_prefix}, b)
            move8 ident2, .ident(.concat(.string(zp_prefix), "2"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident2, .ident(.concat(.string(zp_prefix), "2"))
        .endif
    .endif
    .ifnblank ident3
        .if .xmatch({zp_prefix}, b)
            move8 ident3, .ident(.concat(.string(zp_prefix), "3"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident3, .ident(.concat(.string(zp_prefix), "3"))
        .endif
    .endif
    .ifnblank ident4
        .if .xmatch({zp_prefix}, b)
            move8 ident4, .ident(.concat(.string(zp_prefix), "4"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident4, .ident(.concat(.string(zp_prefix), "4"))
        .endif
    .endif
    .ifnblank ident5
        .if .xmatch({zp_prefix}, b)
            move8 ident5, .ident(.concat(.string(zp_prefix), "5"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident5, .ident(.concat(.string(zp_prefix), "5"))
        .endif
    .endif
    .ifnblank ident6
        .if .xmatch({zp_prefix}, b)
            move8 ident6, .ident(.concat(.string(zp_prefix), "6"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident6, .ident(.concat(.string(zp_prefix), "6"))
        .endif
    .endif
    .ifnblank ident7
        .if .xmatch({zp_prefix}, b)
            move8 ident7, .ident(.concat(.string(zp_prefix), "7"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident7, .ident(.concat(.string(zp_prefix), "7"))
        .endif
    .endif
    .ifnblank ident8
        .if .xmatch({zp_prefix}, b)
            move8 ident8, .ident(.concat(.string(zp_prefix), "8"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident8, .ident(.concat(.string(zp_prefix), "8"))
        .endif
    .endif
    .ifnblank ident9
        .if .xmatch({zp_prefix}, b)
            move8 ident9, .ident(.concat(.string(zp_prefix), "9"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident9, .ident(.concat(.string(zp_prefix), "9"))
        .endif
    .endif
    .ifnblank ident10
        .if .xmatch({zp_prefix}, b)
            move8 ident10, .ident(.concat(.string(zp_prefix), "10"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident10, .ident(.concat(.string(zp_prefix), "10"))
        .endif
    .endif
    .ifnblank ident11
        .if .xmatch({zp_prefix}, b)
            move8 ident11, .ident(.concat(.string(zp_prefix), "11"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident11, .ident(.concat(.string(zp_prefix), "11"))
        .endif
    .endif
    .ifnblank ident12
        .if .xmatch({zp_prefix}, b)
            move8 ident12, .ident(.concat(.string(zp_prefix), "12"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident12, .ident(.concat(.string(zp_prefix), "12"))
        .endif
    .endif
    .ifnblank ident13
        .if .xmatch({zp_prefix}, b)
            move8 ident13, .ident(.concat(.string(zp_prefix), "13"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident13, .ident(.concat(.string(zp_prefix), "13"))
        .endif
    .endif
    .ifnblank ident14
        .if .xmatch({zp_prefix}, b)
            move8 ident14, .ident(.concat(.string(zp_prefix), "14"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident14, .ident(.concat(.string(zp_prefix), "14"))
        .endif
    .endif
    .ifnblank ident15
        .if .xmatch({zp_prefix}, b)
            move8 ident15, .ident(.concat(.string(zp_prefix), "15"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident15, .ident(.concat(.string(zp_prefix), "15"))
        .endif
    .endif
    .ifnblank ident16
        .if .xmatch({zp_prefix}, b)
            move8 ident16, .ident(.concat(.string(zp_prefix), "16"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident16, .ident(.concat(.string(zp_prefix), "16"))
        .endif
    .endif
    .ifnblank ident17
        .if .xmatch({zp_prefix}, b)
            move8 ident17, .ident(.concat(.string(zp_prefix), "17"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident17, .ident(.concat(.string(zp_prefix), "17"))
        .endif
    .endif
    .ifnblank ident18
        .if .xmatch({zp_prefix}, b)
            move8 ident18, .ident(.concat(.string(zp_prefix), "18"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident18, .ident(.concat(.string(zp_prefix), "18"))
        .endif
    .endif
    .ifnblank ident19
        .if .xmatch({zp_prefix}, b)
            move8 ident19, .ident(.concat(.string(zp_prefix), "19"))
        .elseif .xmatch({zp_prefix}, w)
            move16 ident19, .ident(.concat(.string(zp_prefix), "19"))
        .endif
    .endif

.endmacro
When routines call routines, to avoid stepping one each other's local scope, you just leave parameters blank, like this:

Code: Select all

            zp_params b, , , #2, #MAP_FLAG_EJECT_RIGHT
            zp_params w, , , , , , , , ,\   ; <<<the empty spaces correspond to the scope of a routine called by the routine we're about to call
                      #LEFT_POINT0_X, #LEFT_POINT0_Y,\
                      #LEFT_POINT1_X, #LEFT_POINT1_Y
Every so often I forget to do that, but it's a lot easier to maintain than manually assigning equates or zp locations within routines and stepping on each other. You just follow the pattern of using later and later spaces in zp for higher-level routines, and all you have to do is count how many bytes or words are reserved in the inner calls.

I'd rather do this than work on a stack based system, because it results in code I would have manually written anyway---directly accessing zp, no index registers needed.

I think I've heard of others using structs for similar purposes, but I'm not sure how you'd explicitly show that a given routine reserves space for the local variables of routines that it calls, the way this system does.
Post Reply