通过动态值调用子程序

Call subroutine by dynamic value

我是 68k 的新手,我想知道是否可以通过内存中保存的值调用特定的子例程。

伪代码示例:

X: dc.w 0

routine1: 
code
rts

routine2:
more code
rts

代码中的某处类似:

move.w #2,X
JSR routine(X)

执行 routine2,在 routine1

之前 move.w #1,X

我不知道也找不到任何示例,我的猜测是制作一个包含例程的标签,然后使用地址寄存器跳转到特定的偏移量,但不知道如何实现。

欢迎任何帮助!

您正在寻找一个 indirect JSR,它在从地址数组加载寄存器后在寄存器中获取目标地址。 (自从我做任何 m68k 以来已经有很长时间了,但这些是您在指令集参考中寻找的关键字和概念。)更新:请参阅@chtz 的评论。

查找不会按名称进行,您必须在某处使用 dc.l routine1, routine2 来制作 table 的 32 位 函数指针 .

(除非 both/all 例程长度相同,并且您在寄存器中计算跳转目标,如 routine1 + <constant> * index,使用一些 ALU 指令而不是索引到内存中的数组。寻址模式对于 JSR 可以是此计算的一部分;例如 jsr 4(a3) 设置 PC = A3+4).

我不太确定 OP 想要什么。如果你真的想要:

move #2,X
jsr  "routine(X)"

就这样

bsr routine2

如果你想在代码的某些部分决定是稍后调用 routine1 还是 routine2,我会将该地址加载到地址寄存器中并在你需要时调用它(在大多数情况下,您不应该 运行 缺少地址寄存器——但您必须仔细跟踪您在代码的哪些部分使用了哪个寄存器)

; under some condition:
lea  routine1(PC),a4
; under another condition:
lea  routine2(PC),a4

; later:
jsr (a4)

如果您有一个变量(在内存中或在寄存器中)并希望根据其值调用两个子例程之一,请执行一些分支,例如:

  tst.w d0 ; lets assume for d0==0 we call routine1, otherwise routine2
  bne.s \callr2
  bsr   routine1
  bra.s \continue
\callr2:
  bsr   routine2
\continue:
  ; more code

如果 more code 只是一个 rts,请将 bne.s \callr2 替换为 bne routine2,并将 bsr routine1 替换为 bra routine1(即尾调用) .

第三种选择,如果您在 d0 中有一个值范围并且您想根据该值跳转到特定方法,那将是一个跳转-table,它可以是像这样实现(假设所有例程都在 16 位地址范围内——您还需要验证 d0 不包含超出跳转大小的值 table):

  add.w d0,d0                  ; multiply d0 by two
  move.w jumptable(PC,d0.w),d0 ; d0 contains the offset relative to `jumptable` 
  jsr    jumptable(PC,d0.w)    ; do the actual function call
  ; more code -- if this is just a `rts` use `jmp` instead of `jsr`

  ; somewhere else:
jumptable:
  dc.w  routine0-jumptable, routine1-jumptable, routine2-jumptable, ...

另外,如果所有例程的大小都完全相同(理想情况下是 2 的幂——可能在一些填充之后,或者必要时使用一些 trampoline),您也可以直接跳转到类似 PC+offset+d0*size_of_method 的地方:

lsl.w  #4,d0             ; d0 = 16*d0
jsr    routine0(PC,d0.w) ; PC = routine0+d0
; more code

routine0:
   ; exactly 16 bytes of code
routine1:
   ; exactly 16 bytes of code
routine2:
   ; exactly 16 bytes of code
routine3:
   ; (last method could be arbitrary long)

这就是我最终这样做的方式,将 Peter Corder 解决方案与一些外部建议相结合:

  TIMELINE:       DC.L __BLOCK_0,__BLOCK_1,__BLOCK_1
    DC.L __BLOCK_2,__BLOCK_2
    DC.L __BLOCK_2,__BLOCK_3 ... etc


 __BLOCK_0:
   ; SOME CODE
   RTS

-- 在主循环中 --

MOVE.W  P61_LAST_POS,D5
LEA TIMELINE,A3
MULU.W  #4,D5     ; OFFSET IN BYTES
MOVE.L  (A3,D5),A4
JSR (A4)        ; EXECUTE SUBROUTINE BLOCK#

其中 P61_LAST_POS 是随时间变化的增量索引。

这样我就可以通过编辑 LUT“TIMELINE”来控制在任何给定点执行的内容。