我试图理解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也关注我。似乎有更好的方法来处理 没有这种意大利面条代码的多个案例。
答案 0 :(得分:4)
如果案例数足够大,跳转表可能很有用。
在左侧,有一个模板(未经测试),用于跳转到标签,将正确的地址推送到堆栈并返回。在右边有一个 diff 到基于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:
答案 1 :(得分:3)
实际上没有更好的方法,但可能会有一些改进,例如将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
这是怎么做的,不幸的是,没有更好的方法。这适用于许多汇编语言,如果不是全部的话。
答案 2 :(得分:3)
6502提供以下控制程序流程,即修改PC寄存器。
.P
则为RTS)就是这样。如果你想要更复杂的东西,你需要使用上面的一个或多个来创建它。
实现switch语句的一种方法是首先创建一个指向switch语句中涉及的所有例程的指针表。根据例程的低字节然后高字节拆分它们:
switchtab_lo .db >routine1, >routine2, >routine3
switchtab_hi .db <routine1, <routine2, <routine3
(我永远不会记得&gt;意味着低字节或高字节,不同的汇编器可能有不同的语法)
然后,假设您要切换的值是.X,并且vector
是两个不在页面末尾开始的字节(以避免JMP间接错误)并且您确保它是一个有效的值:
lda switchtab_lo,X
sta vector
lda switchtab_hi,X
sta vector+1
jmp (vector)
每次需要切换时这样做很麻烦,但这就是发明高级语言的原因。
答案 3 :(得分:1)
CMOS 6502(即65c02)也具有 JMP(绝对,X)寻址模式,因此您可以将输入取为A,使用ASL A向左移位1位(因为每个表中的地址占用两个位置),然后将其传输到X并执行JMP(Addr_Table,X)。更简单。这是CMOS版本中增加的许多操作码之一。 (CMOS版本还修复了NMOS版本的所有错误。)
答案 4 :(得分:1)
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 ...
答案 5 :(得分:0)
我不知道你如何在6502上执行此操作,但switch
es经常编译为jump tables。
答案 6 :(得分:0)
我有一篇关于在http://wilsonminesco.com/StructureMacros/index.html使用宏在6502程序集中创建程序结构的文章。我也为PIC做过,并且两者的源代码都有链接。当我完成我正在进行的项目时,还会有更多的补充。
对于IF来说,OP是一个完美的情况... ELSE ... END_IF。当需要更多情况时,如果数字是连续的,跳转表运行良好,并且您不必测试限制以避免从表外跳出间接并且崩溃。否则CASE语句很好用。 http://forum.6502.org/viewtopic.php?f=2&t=2311&start=15是这种讨论的第二页,我在那里展示了如何在同一个CASE语句中测试个别情况,一系列数字或数字散布。我还没有将RANGE_OF和SET_OF写成6502汇编宏。这是我在Forth唯一的两个。
这些宏的目标当然是通过更清楚地了解您正在做什么,以及摆脱通常表征汇编代码的大量标签和跳转来更好地控制代码。这些宏可以让你完全控制汇编程序编写的每一段代码,但是你不必继续查看内部细节。在大多数情况下,程序存储器或执行速度绝对没有任何损失。您可以获得具有高级语言许多优点的程序集性能。代码变得更快,更无错误,更易于维护,这意味着以后更容易回来并弄清楚当您决定添加功能或更改某些内容时所执行的操作。