Search code examples
assemblyparameter-passingcalling-convention6502c64

Passing memory address to subroutine in C64 Assembly?


param word 0

function
    ...
    lda param,x


scr1 byte "some text"
     byte 0

So how do I pass "scr1" to function as "param"? I know it's a memory address, so it doesn't fit into 1 byte registers. What's the best way to do this?

EDIT:

Thanks for the answers! The solution posted below works great, but this uses both X and Y registers. What if "function" calls KERNAL routines which rely on X or Y or my own code needs X for something etc? In these case this won't work as expected I guess?

I'm totally new to assembler, so I'm confused about many things.

Basically I wanted to pass the address, because I wanted to avoid code duplication. I have a simple function which prints a string to the screen like so:

        ldx #$00
copy_text
        lda scr1,x
        beq done
        sta screen_start,x
        inx
        bne copy_text
done
        rts

But this works only with scr1. If wanna print other stuff, I need to duplicate the code which seems to be wasteful.

Is that acceptable in assembler? I mean, in C or any other language you would just write a reusable function or method. But in assembler this seems to be very hard to do, because there are only 3 registers and they are used by many things.

What's the best way to overcome this limitation?


Solution

  • There are a few ways to do this.

    zpb = $fb
    
    function = *
       stx zpb+0  
       sty zpb+1  ; note after you've stored .X and .Y in zero page they can be re-used for other purposes.
       ...
       ldy #$00
       lda (zpb),y
       ...
    
    caller = *
        ldx #<scr1
        ldy #>scr1
        jsr function
    

    Alternatively you can play with the stack

    zpb = $fb
    
    function = * 
       pla            ; save current stack pointers for later
       sta temp+0
       pla
       sta temp+1
       pla            ; pull low byte off first
       sta zpb+0
       pla            ; now pull off high byte
       sta zpb+1       
       lda temp+1     ; restore stack pointers so RTS works as expected
       pha
       lda temp+0
       pha
       ...
       ldy #$00
       lda (zpb),y
       ...
    temp .byte 0,0
    
    caller = *
        lda #>scr1   ; note we push high byte first since stack is FILO
        pha
        lda #<scr1
        pha
        jsr function
    

    Machine language by its own on the 6502 only has the three registers, so your options are usually to pass values in those registers, or, use those registers to point to a larger set of data that you can then access elsewhere. The indirect zero page is your best friend in this regard since you can then store pointers in zero page and then access them indirectly.