Assembly 6502中有哪些使用控制流的方法?
我试图了解6502组件中的控制流程 假设我有以下代码:Assembly 6502中有哪些使用控制流的方法?,assembly,6502,retro-computing,Assembly,6502,Retro Computing,我试图了解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 ex
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
控制流:
lda mem
cmp#1
B.sub_一
cmp#2
第二部分
.sub_one:
; 子程序一在这里
jmp.done 我不知道如何在6502上实现这一点,但是开关
es通常被编译成。实际上没有更好的方法,但可能会有改进,比如将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
事情就是这样做的,不幸的是,没有更好的办法。这适用于许多汇编语言(如果不是全部的话)。如果案例数量足够大,跳转表可能会很有用。
在左边,有一个跳转到标签的模板(未测试),它将正确的地址推送到堆栈并返回。右侧有一个基于jsr的diff-to-jsr例程,从每个子例程返回后,该例程将在标签\u out:
处继续。
进位逻辑在6502上反转,这意味着如果(Acc>=Imm),将设置进位
我有一篇关于在6502汇编中使用宏生成程序结构的文章,位于。我也为PIC做了这件事,链接中有两个的源代码。当我完成一个我正在做的项目时,会有更多的补充
OP是IF…ELSE…END_IF的完美场景。当需要更多的情况时,如果数字是连续的,跳转表工作得很好,并且您不必测试限制,以避免从表外间接跳转和崩溃。否则,CASE语句可以很好地工作。这是讨论的第二页,我在这里展示了如何在同一个案例陈述中测试单个案例、一系列数字或分散的数字。我还没有编写6502汇编宏的范围和集合。这是我仅有的两个
当然,这些宏的目标是更好地控制代码,方法是让代码更清楚地显示您在做什么,并消除通常作为汇编代码特征的大量标签和跳转。宏让您能够完全控制汇编程序所编写的每一段代码,但您不必一直查看内部细节。在大多数情况下,程序内存或执行速度绝对为零。您可以从高级语言的许多好处中获得汇编的性能。代码开发速度更快,无bug,维护更容易,这意味着以后更容易返回并了解您在决定添加功能或更改某些内容时所做的操作。6502提供了以下内容来控制程序流,即修改PC寄存器
- 绝对JMP
- JMP间接
- Bxx相关说明
- JSR绝对值,具有更高的RTS
- BRK或其他具有更高RTI的IRQ(如果您将
.P
从堆栈中拉出,则为RTS)
- 将两个值推送到堆栈,然后是RTS/RTI
- 硬件重置导致跳转通过重置向量
就这样。如果您想要更复杂的东西,您需要使用上面的一个或多个来创建它
实现switch语句的一种方法是首先创建一个指向switch语句中涉及的所有例程的指针表。根据例程的低字节和高字节对其进行拆分:
switchtab\u lo.db>routine1、>routine2、>routine3
switchtab_hi.dbCMOS 6502(即65c02)也具有JMP(abs,X)寻址模式,因此您可以在A中获取输入,使用ASL A左移1位(因为表中的每个地址占据两个位置),然后将其传输到X并执行JMP(Addr_表,X)。简单得多。这是CMOS版本中添加的许多操作码之一。(CMOS版本还修复了NMOS版本的所有错误)
如果向量列表未对齐,on需要将索引*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 ...
你在用汇编语言编程。你必须自己建造所有奇特的控制结构。如果您想要其他人编写的控制结构,那么请使用高级语言。当然,您也可以扩展跳转表,使无效索引直接跳转到out
。如果您的案例足够稀疏,您可以将索引直接写入分支,分支到精心编写的分派表中。如果已知案例数量有限,则可以。进一步的改进将从第0页起为表偏移量指定2个字节,并确保每个开关盒标签位于同一页上。在这种情况下,也可以使用自修改的相对地址。+1(嘿,在将我的注释添加到上面的注释后阅读此答案。)table_hi
和table_lo
在上面的示例中必须包含跳转地址减1。这是因为jsr
不推送返回地址本身,而是推送返回地址减去一,因此rts
在到达之前向弹出的地址添加一个。这是一种非常低效的跳转表构造方法,使用了许多测试+分支对。肯定有比这更好的方法。JasonD,请分享你的替代解决方案,然后我们可以讨论。作者试图摆脱常规结束跳转以避免spagetti代码,我的解决方案展示了如何做到这一点。如果他试图用更少的周期或更短的代码进行分支,我会给出不同的建议。对于这样的场景,你的“绝对更好的方法”是什么?至少有两个改进是我能想到的;首先,使用带有“mem”值的ZP间接寻址作为子程序表中的偏移量,可以消除CMP系列。第二,加强这一点
lda switchtab_lo,X
sta vector
lda switchtab_hi,X
sta vector+1
jmp (vector)
lda mem
asl
sta jump+1
jump jmp (vector)
;should be page aligned
vector
!word func1, func2, func3, func4
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 ...