Assembly 按动态值调用子例程

Assembly 按动态值调用子例程,assembly,function-pointers,subroutine,68000,Assembly,Function Pointers,Subroutine,68000,我对68k很陌生,我想知道是否可以通过内存中的值调用特定的子例程 伪代码示例: X: dc.w 0 routine1: code rts routine2: more code rts 代码中的某个地方类似于: move.w #2,X JSR routine(X) tst.w d0 ; lets assume for d0==0 we call routine1, otherwise routine2 bne.s \callr2 bsr routine1 bra.s

我对68k很陌生,我想知道是否可以通过内存中的值调用特定的子例程

伪代码示例:

X: dc.w 0

routine1: 
code
rts

routine2:
more code
rts
代码中的某个地方类似于:

move.w #2,X
JSR routine(X)
  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
要执行routine2,请在routine1之前执行ore
move.w#1,X

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

欢迎任何帮助

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

查找不会按名称进行,您必须在某个地方使用
dc.l routine1、routine2
来创建一个包含32位函数指针的表


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

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

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

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

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

move.w #2,X
JSR routine(X)
  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
中有一系列值,并且希望根据该值分支到特定的方法,那么这将是一个跳转表,可以这样实现(假设所有例程都在16位地址范围内——您还需要验证
d0
不包含超出跳转表大小的值):

此外,如果所有例程的大小都完全相同(理想情况下是2的幂——可能在一些填充之后,或者在必要时使用一些蹦床),您也可以直接跳到类似于
PC+offset+d0*size\u of\u方法的东西上

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
--主回路中--

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


通过这种方式,我可以通过编辑LUT“时间线”来控制在任何给定点执行的操作。

OK,那么在加载ex.A3中的表地址后,我应该能够用JSR1(A3)触发routine2了?@KONEY:记住,在汇编中,所有操作都使用字节偏移量。所以您需要
JSR4(A3)
,就像索引32位整数数组一样。(因为指针是32位整数)。
jsr4(A3)
将调用从地址
A3+4开始的函数。您可能需要
movea.l4(A3),A3;jsr(A3)
如果跳转表位于代码内部,并且索引位于
D0
中,则可以使用移位将
D0
乘以4,并使用PC相对寻址来获得跳转地址:
lsl.w#2,D0;movea.l表(PC,D0.w),A3;jsr(A3);
如果您的例程在同一个二进制中,您甚至可以只存储相对于某个标签的16位偏移量。@chtz:谢谢,有趣的设计选择是让JSR的非寄存器操作数使用寻址模式进行计算跳转,而不是x86选择作为从该内存位置加载地址的内存间接跳转/调用。更新了一点我的答案,请随意编辑自己并加入实际的代码建议。或者发布一个单独的答案,我会投票表决。是的,
jsrx
基本上就像
leax,PC
而不是
move X,PC
(对我来说这看起来更自然,但我在学习x86之前很久就学会了68k汇编程序,所以我有点偏见)。另一种方法当然也有意义,但可能不适用于其他一些设计决策——例如,立即数不是有效地址(这就是为什么许多其他指令都有额外的操作码,如
andi
addi
,等等。)因此,需要另一个
jmp
/
jsr
操作码来编码跳转到绝对地址。您不应该在问题中编辑答案,而应该接受给定答案或提供自己的答案(最终您也可以接受)。此外,您的解决方案还允许进行多次优化…使用左移乘以2的幂,永不
mulu
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#