问题描述
我目前正在尝试弄清楚如何将指针寄存器 SI
指向的内存中的第一个字节添加到 AX
寄存器的当前内容中。
因此,如果 SI
包含某个地址,并且该地址的内存中的值为:00 和 01,我希望仅将 00 添加到 AX
寄存器中。
我不喜欢汇编的自己尝试的第一条指令是 add ax,byte ptr [SI]
但当然没有骰子,因为我正在尝试添加不同大小的操作数。
我目前的解决方法是
mov dx,0000h ;empty the contents of dx
mov dl,byte ptr [si] ;get the value of the first byte in a register
add ax,dx ;perform the originally desired addition
但这是非常浪费的,并且真的损害了我执行的指令计数(这是运行多次的子程序的一部分)。
我只能使用 8086 指令集,因此 this question/answer by Peter Cordes 建议使用 movzx
来压缩我的前两行,但不幸的是不可行。
解决方法
如您所说,如果您可以假设 CPU 兼容 386,那么一个不错的选择(尤其是对于现代 CPU)是 movzx dx,byte ptr [mem]
/ add ax,dx
。如果没有,我想我们可以假装我们正在调整一个真正的 8086,where code size in bytes is often more important than instruction count。 (特别是在 8088 上,它的 8 位总线。)所以你肯定想使用 xor dx,dx
来归零 DX(2 个字节而不是 3 个用于 mov reg,imm16
),如果你不能避免归零指令一共。
从任何循环中提升 DX(或 DH)的归零,因此您只需 mov dl,[mem]
/ add ax,dx
。如果函数只执行一次,您可能需要(手动)
在循环调用它的调用站点中内联该函数,如果它足够小以使其有意义。或者选择一个寄存器,调用者负责将上半部分为零。
正如 Raymond 所说,您可以选择任何其他寄存器,在您的函数中,您知道其高半部分在该点为零。也许您可以用 mov cx,4
代替 mov cl,4
,如果您之前碰巧需要 CL=4 用于其他东西,但是当您需要添加到 AX 时,您已经完成了 CX。 mov cx,4
仅长 1 个字节,因此您只需 1 个额外字节的代码大小即可将 CH 清零。 (相对于 xor cx,cx
花费 2 个字节)
另一个选项是字节添加/adc,但这对于代码大小来说并不理想。 (或在更高版本的 CPU 上的性能。)
add al,[mem] ; 2 bytes + extra depending on addr mode
adc ah,0 ; 3 bytes
所以这比如果你已经有一个备用的高位零寄存器多 1 个字节:
mov dl,[mem] ; 2 bytes (+ optional displacement)
add ax,dx ; 2 bytes
但从好的方面来说,add/adc 根本不需要任何额外的寄存器。
使用 SI 中的指针,如果您真的要优化代码大小,那么寻找利用 lodsb
的方法是值得的。这样做 mov al,[si]
/ inc si
(或者如果 DF=1 则改为 dec si
),但不会影响 FLAGS。所以你想添加到不同的寄存器中。
xchg ax,reg
只有 1 个字节,但如果您需要两次交换,如果您实际上必须返回 AX 而不是其他寄存器,则可能无法收回成本。