在6502汇编中编写子程序的最佳方法是什么?

4

我对汇编语言不太熟悉,所以这里有一个简单的问题:

我的自定义子例程会更改XYA 寄存器。它们通过操作这些寄存器来产生期望的结果。在例程开始时将这些值推送到堆栈中并在RTS之前还原,这样做是否明智?

我的意思是,这样我可以编写可从任何地方调用的子例程,而不会弄乱“状态”或影响其他子例程。但是这样使用堆栈是否正确?或者有更好的方法吗?


2
这完全取决于您想要实现哪种调用约定。如果您定义了函数覆盖某个特定寄存器,并且您可以编写所有的调用者以考虑到这一点,那么您可以自由选择。 - ecm
3
在旧的机器上,常常会针对每个函数使用自定义且可能不同的调用约定,这基于其签名(参数和返回值)和内部实现。在汇编语言中编程时,这种做法很实用,但在C语言中变得更加困难。 - Erik Eidt
1
在C64中,通常使用零页中的固定地址来传递一些参数,特别是16位地址。 - Aki Suihkonen
有很多种方法可以做到这一点。推送是完全可以的。这里有一个好的阅读材料供您参考。https://wilsonminesco.com/stacks/ - puppydrum64
1个回答

5

但是这样使用堆栈可以吗?或者有更好的方法吗?

当然可以,BASIC经常这样做,内核中也有很多例程这样做。

但是,没有正确的答案,这取决于速度、可移植性和风格等因素。

  • 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.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接