4

我正在尝试了解 6502 程序集中的控制流程。

假设我有以下代码:

    ControlFlow:
      lda mem
      cmp #1
      bne .sub_one

      cmp #2
      bne .sub_two

      .sub_one:
        ; sub routine one goes here
        jmp .done ; <-------------- without this jmp, .sub_two will execute

      .sub_two:
       ; sub routine two goes here

      .done:

      rts

就个人而言,我喜欢 switch 语句或其他一些控制流结构。上面的 JMP 也与我有关。似乎有一种更好的方法可以在没有这种意大利面条代码的情况下处理多种情况。

4

7 回答 7

5

如果案例数量足够大,跳转表会很有用。在左侧,有一个模板(未经测试)用于跳转到标签,它将正确的地址推送到堆栈并返回。右侧有一个基于 jsr 的例程的差异,它将在_out:从每个子例程返回后在标签处继续。进位逻辑在 6502 上反转,这意味着如果 (Acc >= Imm) 将设置进位。

; goto  label[n]   vs.         call label[n]

lda variable
cmp #MAX_PLUS_ONE                          
bcs _out
tax
lda table_hi, X
pha                vs.         sta jsrcmd+2
lda table_lo, X
pha                vs.         sta jsrcmd+1
rts                vs. jsrcmd: jsr 1000        ; self modify

_out:  
于 2013-01-20T09:20:38.627 回答
4

实际上没有更好的方法,但可能会有改进,例如将 FlowControl 作为子例程调用并使用 RTS 返回。

这是主要流程。

  jsr ControlFlow
  ; main routine continues here

这是子程序。

ControlFlow:
  lda mem
  cmp #1
  bne .sub_one
  cmp #2
  bne .sub_two
  cmp #3
  bne .sub_three
    ; case else here
  rts

  .sub_one:
    ; sub routine one goes here
  rts

  .sub_two:
   ; sub routine two goes here
  rts

  .sub_three:
   ; sub routine three goes here
  rts

如果子例程太长,则需要使用前面提到的 JMP。

.jump_to_sub_one
  jmp .sub_one
.jump_to_sub_two
  jmp .sub_two
.jump_to_sub_three
  jmp .sub_three

ControlFlow:
  lda mem
  cmp #1
  bne .jump_to_sub_one
  cmp #2
  bne .jump_to_sub_two
  cmp #3
  bne .jump_to_sub_three
    ; case else here
  rts

  .sub_one:
    ; sub routine one goes here
  rts

  .sub_two:
   ; sub routine two goes here
  rts

  .sub_three:
   ; sub routine three goes here
  rts

这就是它的完成方式,不幸的是,没有更好的方法。这适用于许多汇编语言,如果不是全部的话。

于 2013-01-19T09:29:14.430 回答
3

6502提供以下控制程序流程,即修改PC寄存器。

  • JMP绝对
  • JMP 间接
  • bxx 相关说明
  • JSR 绝对值与更高版本的 RTS
  • BRK 或其他带有稍后 RTI 的 IRQ(或 RTS,如果您.P从堆栈中拉出)
  • 将两个值推入堆栈,然后推入 RTS/RTI
  • 硬件复位导致跳过复位向量

就是这样。如果您想要更复杂的东西,您需要使用上述一种或多种方法来创建它。

实现 switch 语句的一种方法是首先创建一个指向 switch 语句中涉及的所有例程的指针表。根据例程的低字节和高字节拆分它们:

switchtab_lo .db >routine1, >routine2, >routine3

switchtab_hi .db <routine1, <routine2, <routine3

(我永远不记得 > 是指低字节还是高字节,不同的汇编程序可能有不同的语法)

然后,假设您要切换的值在 .X 中,即vector不是从页面末尾开始的两个字节(以避免 JMP 间接错误),并且您已确保它是有效值:

lda switchtab_lo,X
sta vector
lda switchtab_hi,X
sta vector+1
jmp (vector)

每次需要切换时都这样做很乏味,但这就是发明高级语言的原因。

于 2013-08-18T14:27:06.133 回答
2
        lda mem
        asl
        sta jump+1
jump    jmp (vector)

;should be page aligned
vector
        !word func1, func2, func3, func4

如果向量列表未对齐,则需要将索引*2 添加到整个向量地址,速度较慢,但​​内存效率更高。

其他选择是:

         lda mem
         asl
         clc
         adc mem        ;we assume it does not overflow, so carry stays cleared
         sta branch+1   ;mem * 3
branch   bcc *
         jmp func1
         jmp func2
         jmp func3
         jmp ...
于 2015-10-21T06:58:36.533 回答
1

CMOS 6502(即65c02)也有JMP(abs,X)寻址方式,所以可以取A中的输入,用ASL A左移1位,(因为表中的每个地址占用两个位置),然后将其转移到 X 并执行 JMP(Addr_Table,X)。简单得多。这是 CMOS 版本中添加的许多操作码之一。(CMOS版本也修复了NMOS版本的所有bug)。

于 2013-11-22T22:11:46.003 回答
0

我不知道你在 6502 上怎么做,但是switches 经常被编译成jump tables

于 2013-01-19T04:15:45.280 回答
0

我在http://wilsonminesco.com/StructureMacros/index.html上有一篇关于使用宏在 6502 汇编中制作程序结构的文章。我也为 PIC 完成了它,并且两者的源代码的链接都在那里。当我完成我正在做的一个项目时,还会有更多的补充。

OP 是 IF...ELSE...END_IF 的理想选择。当需要更多情况时,如果数字是连续的,则跳转表效果很好,并且您不必测试限制以避免从表外间接跳转并崩溃。否则 CASE 语句可以很好地工作。 http://forum.6502.org/viewtopic.php?f=2&t=2311&start=15是此类讨论的第二页,我在那里展示了如何测试个别情况、一系列数字或分散数字,都在同一个 CASE 语句中。我还没有将 RANGE_OF 和 SET_OF 写成 6502 汇编宏。那是我只在 Forth 中拥有的两个。

当然,这些宏的目标是通过更清楚地了解您在做什么,并摆脱通常表征汇编代码的大量标签和跳转,从而更好地控制代码。宏让您可以完全控制汇编程序编写的每一段代码,但您不必一直查看内部细节。在大多数情况下,占用的程序内存或执行速度绝对为零。您可以获得汇编的性能以及高级语言的许多好处。代码变得更快开发、更无缺陷且更易于维护,这意味着当您决定添加功能或更改某些内容时,更容易稍后返回并弄清楚您做了什么。

于 2013-03-04T04:10:17.803 回答