【问题标题】:Call subroutine by dynamic value通过动态值调用子程序
【发布时间】:2020-11-09 15:01:56
【问题描述】:

我对 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

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

欢迎任何帮助!

【问题讨论】:

  • 您应该接受给定的答案或提供自己的答案(最终您也可以接受),而不是在您的问题中编辑答案。此外,您的解决方案允许进行多项优化...

标签: assembly function-pointers subroutine 68000


【解决方案1】:

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

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

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

【讨论】:

  • OK 所以在 ex. 中加载表地址后。 A3 我应该能够用 jsr 1(A3) 触发routine2?
  • @KONEY:请记住,在汇编中,所有内容都使用字节偏移量。所以你需要jsr 4(A3),就像索引一个32位整数数组一样。 (因为指针 32位整数)。
  • jsr 4(A3) 将调用从地址A3+4 开始的函数。您可能想要movea.l 4(A3),A3; jsr (A3); 如果您的跳转表在您的代码中,并且您在D0 中有索引,您可以使用移位将D0 乘以4 并使用PC 相对寻址来获得跳转地址:@ 987654330@ 如果您的例程在同一个二进制文件中,您甚至可以只存储相对于某个标签的 16 位偏移量。
  • @chtz:谢谢,有趣的设计选择是让 JSR 的非寄存器操作数使用寻址模式进行计算跳转,而不是 x86 选择作为加载地址的内存间接跳转/调用从那个记忆位置。稍微更新了我的答案,请随时编辑自己并输入您的实际代码建议。或者发布一个单独的答案,我会投赞成票。
  • 是的,jsr X 本质上类似于lea X, PC 而不是move X, PC(对我来说这看起来更自然,但我在学习 x86 之前很久就学习了 68k 汇编程序,所以我有点偏见)。另一种方式当然也有意义,但可能不适合其他一些设计决策——例如,立即数不是有效的有效地址(这就是为什么许多其他指令有额外的操作码,如andi、@987654335 @ 等),因此需要另一个 jmp/jsr 操作码来编码跳转到绝对地址。
【解决方案2】:

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

  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 的幂 - 可能在一些填充之后,或者在必要时使用一些蹦床),您也可以直接跳转到类似 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)

【讨论】:

    【解决方案3】:

    这就是我最终这样做的方式,将 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”,就可以控制在任何给定点执行的操作。

    【讨论】:

    • 使用左移乘以 2 的幂,而不是 mulu
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 2013-03-28
    相关资源
    最近更新 更多