我正在用汇编语言为 Motorola 68000 编写一个子程序,用于连接两个字符串。该子程序接收两个输入字符串StringA
("Hello") 和StringB
("World"),并将连接结果存储在StringC
("HelloWorld") 中。
代码编译时没有错误,似乎可以工作,但我不确定输出是否正确或实现是否在逻辑上正确。
我写了以下代码:
ORG $8000
StringA DC.B 'Hello',0 ; First string with null terminator
StringB DC.B 'World',0 ; Second string with null terminator
StringC DS.B 20 ; Buffer for the concatenated string (large enough?)
START:
lea.l StringA,a0 ; a0 -> "Hello"
lea.l StringB,a1 ; a1 -> "World"
lea.l StringC,a2 ; a2 -> Buffer for concatenation
clr.b d0
jsr CopyA ; Call first subroutine
SIMHALT
CopyA:
move.b (a0)+,d0 ; Load character from StringA into d0
; Check if it is the null terminator
beq.s CopyB ; If yes, start copying StringB
move.b d0,(a2)+ ; Otherwise, copy character into StringC
bra CopyA
CopyB:
move.b (a1)+,d0 ; Load character from StringB into d0
move.b d0, (a2)+ ; Copy it into StringC
bne CopyB ; If the character is not null, continue copying
rts ; Return from subroutine
END START
问题:
- 我的子程序实现正确吗?我使用了
jsr
和rts
,但我想确认这是否是此上下文中的最佳方法。 - 可以以任何方式进行优化吗?例如,我可以删除哪些冗余指令?
68k 上的指令
move
特别强大。他们为该move
指令提供了最少的操作码位,保留了 16 位指令字的许多位,以便它可以支持两个<ea>
操作数。在 68k 中,一个<ea>
操作数(有效地址)占用 6 位,因此其中两个意味着 12 位,只剩下 4 位用于其他内容。鉴于大小选择占用 2 位(字节/字/长),移动操作码本身剩下高达 2 位。由于设计人员将两个
<ea>
操作数压缩到该指令中,这意味着move
可以将内存移动到内存,同时还可以设置条件代码!将其
add
与大多数其他 68k 指令进行比较:这些指令仅支持一个<ea>
操作数和一个数据(或地址)寄存器或一个立即数作为另一个操作数。因此,字符串复制应使用类似
move (a0)+, (a2)+
¹的指令来完成。由于此指令设置了条件代码,因此您可以直接在其后跟一个条件分支(例如,在非零/非空时向后)。
该算法必须稍微改变(如@SepRoland 的建议所示),因为使用这种移动形式,第一个字符串中的空字节将被复制到目标缓冲区中,因此
a2
在复制第二个字符串之前必须备份 1 个字节(但这次你确实希望复制的空字节来终止字符串 C,因此move (a1)+,(a2)+
是完美的并且不需要任何调整)。¹还有一条
dbcc
指令,可用于形成一个两条指令循环,该循环移动受计数和空终止符限制的内存 - 这些指令在 68010 上运行速度特别快,因为它具有dbcc
通过一条指令向后分支的模式,无需提取指令即可运行。 (后来的 68k 具有更通用的指令缓存,因此可以在没有指令提取的情况下执行更大的循环。)调试器的屏幕显示了什么?如果某处有“HelloWorld”,那么它似乎已经成功了,不是吗?
只需将两个长度相加,并允许一个额外的字节用于结束零即可。因此,这里 11 个字节就足够了。但我们不要吝啬,为通用缓冲区分配一个合适的大小。如果您使用 2 的幂数,那么它就是非常好的“汇编风格”。
最好的?其实不是,因为你的子程序非常接近调用点(距离从 -128 到 +127),你可以使用
bsr.s
指令以更少的字节到达它。它在指令字中内置了相对位移,而不像jsr
必须在指令字后添加它。rts
其余的就像你写的一样。这并不快,但如果需要位置无关的代码(没有绝对内存引用),这是必需的。clr.b d0
调用子程序之前的内容似乎是多余的。bra CopyA
。我建议您以类似于处理第二个字符串的方式处理第一个字符串,但在两次复制操作之间从 A2 中减去 1。这将删除零字节,只需用第二个字符串的第一个字符覆盖它即可(如果第二个字符串为空,则为零)。这是一个有趣的变化:
对于第一个字符串,本地Copy子程序会正常调用,而对于第二个字符串,执行会直接进入这些Copy指令。第一次执行会
rts
返回本地,但第二次执行会rts
返回主程序。