4

I'm new to assemblers, so here is a simple question:

My custom subroutines change the X, Y, and A registers. They manipulate these to produce the desired results. Is it a good idea to push these values to the stack when the routine starts and restore them before RTS?

I mean, this way I can write routines which can be called from anywhere without messing up the "state" or affecting other routines. But is it OK to use the stack this way? Or is there a better way to do this?

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
user2297996
  • 1,382
  • 3
  • 18
  • 28
  • 2
    It all depends on what calling convention you want to implement. If you define that your function overwrites a certain register, and you can write all callers so as to take this into account, you can make the choice either way. – ecm Oct 10 '21 at 13:59
  • 3
    It is common on older machines to use a custom and potentially different calling convention for each function based on both its signature (parameters & return value) and internal implementation -- this is practical when programming in assembly, and gets more difficult in C. – Erik Eidt Oct 10 '21 at 15:20
  • 1
    In C64 it makes typically sense to use fixed addresses in zero page for passing some parameters, especially 16-bit addresses. – Aki Suihkonen Oct 11 '21 at 13:34
  • There's quite a few ways to do it. Pushing is totally fine. Here's a good read for you. https://wilsonminesco.com/stacks/ – puppydrum64 Feb 06 '23 at 15:55

1 Answers1

5

But is it OK to use the stack this way? Or is there a better way to do this?

Absolutely; BASIC does it all the time, as do many routines in the kernal.

But, there is no right answer to this, it comes down to at least speed, portability, and style.

  • If you use the stack a lot, there are some speed considerations. Your typical pha txa pha tya pha at the start, and then the reverse (pla tay pla tax pla) eats up 3 bytes of your stack, and adds in some cycle time due to the 2 x 5 operations

  • You could use zero page, but that takes away some portability between different machines; VIC-20, C64, C128, the free zero page addresses may not be the same across platforms. And your routine can't be called "more than once" without exiting first (e.g. no recursion) because if it is called while it is active, it will overwrite zero page with new values. But, you don't need to use zero page...

  • ...because you can just create your own memory locations as part of your code:

    myroutine = *
        ; do some stuff..
        rts
    
    mymem =*
        .byt 0, 0, 0
    
  • the downside to this is that your routine can only be called "once", otherwise subsequent calls will overwrite your storage areas (e.g. no recursion allowed!!, same problem as before!)

  • You could write your own mini-stack

      put_registers =*
         sei ; turn off interrupts so we make this atomic
         sty temp
         ldy index
         sta a_reg,y
         stx x_reg,y
         lda temp
         sta y_reg,y
         inc index
         cli
         rts
    
      get_registers =*
         sei ; turn off interrupts so we make this atomic
         dec index
         ldy index
         lda y_reg,y
         sta temp
         lda a_reg,y
         ldx x_reg,y
         ldy temp
         cli
         rts
    
      a_reg .buf 256
      x_reg .buf 256
      y_reg .buf 256
      index .byt 0
      temp  .byt 0
    
  • This has the added benefit that you now have 3 virtual stacks (one for each of .A, .X, .Y), but at a cost (not exactly a quick routine). And because we are using SEI and CLI, you may need to re-think this if doing it from an interrupt handler. But this also keeps the "true" stack clean and more than triples your available space.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
tendim
  • 447
  • 3
  • 10