如何在汇编中创建幂函数?
How can I create a power function in assembly?
我正在尝试编写一个汇编函数,将 ASCII 十进制数字转换为计算机可以轻松使用的十六进制值。在展示我的代码之前,我必须指出我在 16 位实模式下工作(使用 AT&T 语法),因此我有一些限制。
为了实现我的目标,我写了一个“power”函数,它有两个参数(BX 作为基数,CX 作为指数)和 returns BX^ CX 到 BX,或者至少这是我所期望的。实际上,它只工作一次。例如:
mov , %cx
mov , %bx
call power
mov %bx, %dx
call printh # Prints the value in dx
dec %cx
mov , %bx
call power
mov %bx, %dx
call printh # Prints the value in dx
这里前三行工作正常,因为它们将 100 (0x64) 放入 BX。相反,其他三行将 10 (0xA) 放入 BX 的最后 4 位,从 0x64 保留旧的 (0x60),结果产生错误的 0x6A。
我做了很多检查,所以我很确定这就是问题的本质。尽管如此,我还是想不出解决办法。而且,我的“power”函数的代码实在是多余,我想简化一下。这是:
power:
push %ax
push %cx
mov %bx, %ax
or %cx, %cx
jz power_zero
power_loop:
cmp , %cx
je power_loop_end
mul %bx
dec %cx
jmp power_loop
power_loop_end:
mov %ax, %bx
pop %cx
pop %ax
ret
power_zero:
mov , %bx
jmp power_loop_end
最近,在 qemu 中组装和尝试我的代码后,我得到了这个奇怪的错误:
我不知道它是否与我的电源函数中的这个问题有某种联系,因为在出现这个错误之前我也有这个问题。最后,我坚决排除错误出在我的打印函数上,我试了很多次都成功了。
我想知道你们中是否有人知道如何解决这个问题,最重要的是,如果有人能告诉我发生了什么,我的计算机如何记住一个值(例如 0x60 来自第一次上电)我通过 mov 或 xor 操作擦除。
如果你想自己尝试代码,我在这里放了一个我试过的完全可执行的代码:https://github.com/LoZack19/bare_metal/blob/main/power/power.S
编辑:
在阅读了您的答案后,我认为最好在我的问题中也包括 printh 函数的代码,因为正如您正确指出的那样,错误就在这里,并且 power 函数没有问题:
# printh(%dx)
# dx: hex value to print
# ==> converts the value stored in dx to ascii and prints it
printh:
pusha
mov [=12=]x05, %cx
loop_printh:
cmp [=12=]x00, %dx
je end_loop_printh
call convert # Converts the lower nibble (4-bit) of dx into an ascii character and stores it in HEX_OUT
dec %cx
shr [=12=]x04, %dx
jmp loop_printh
end_loop_printh:
mov $HEX_OUT, %si
call printf
popa
ret
# [...]
HEX_OUT: .asciz "0x0000"
power 函数在这种情况下就很好(除了评论中提到的不相关的错误)。当使用 bx = 10
和 cx = 1
调用时,它会正确地将 10
留在 bx
中。 Single-stepping 在调试器中进行测试是一个很好的测试方法,我发现 Bochs 是一个比 qemu 更方便调试 16 位代码的模拟器。
错误出在您的“坚决排除”打印函数中,特别是在 printh
。考虑当 dx
包含小于或等于 0xf
的值时会发生什么。 loop_printh
的第一次迭代后,低半字节已移出 dx
,因此 dx
现在为 0。因此 loop_printh
处的测试将退出循环第二次迭代。所以高 3 个半字节永远不会被写入 HEX_OUT
,因此它仍然包含任何字符作为前一个打印的高半字节留下的任何字符,这里是 006
.
无论输入如何,您的 printh
都需要将循环迭代四次,以确保为所有高半字节写入 0
。因此,您对循环的测试可能希望基于计数器 cx
的值。
how can my computer remember a value (such as the 0x60 from the first power call) I erased through a mov or a xor operation.
因为你从未真正删除过它:-)
我正在尝试编写一个汇编函数,将 ASCII 十进制数字转换为计算机可以轻松使用的十六进制值。在展示我的代码之前,我必须指出我在 16 位实模式下工作(使用 AT&T 语法),因此我有一些限制。
为了实现我的目标,我写了一个“power”函数,它有两个参数(BX 作为基数,CX 作为指数)和 returns BX^ CX 到 BX,或者至少这是我所期望的。实际上,它只工作一次。例如:
mov , %cx
mov , %bx
call power
mov %bx, %dx
call printh # Prints the value in dx
dec %cx
mov , %bx
call power
mov %bx, %dx
call printh # Prints the value in dx
这里前三行工作正常,因为它们将 100 (0x64) 放入 BX。相反,其他三行将 10 (0xA) 放入 BX 的最后 4 位,从 0x64 保留旧的 (0x60),结果产生错误的 0x6A。 我做了很多检查,所以我很确定这就是问题的本质。尽管如此,我还是想不出解决办法。而且,我的“power”函数的代码实在是多余,我想简化一下。这是:
power:
push %ax
push %cx
mov %bx, %ax
or %cx, %cx
jz power_zero
power_loop:
cmp , %cx
je power_loop_end
mul %bx
dec %cx
jmp power_loop
power_loop_end:
mov %ax, %bx
pop %cx
pop %ax
ret
power_zero:
mov , %bx
jmp power_loop_end
最近,在 qemu 中组装和尝试我的代码后,我得到了这个奇怪的错误:
我不知道它是否与我的电源函数中的这个问题有某种联系,因为在出现这个错误之前我也有这个问题。最后,我坚决排除错误出在我的打印函数上,我试了很多次都成功了。
我想知道你们中是否有人知道如何解决这个问题,最重要的是,如果有人能告诉我发生了什么,我的计算机如何记住一个值(例如 0x60 来自第一次上电)我通过 mov 或 xor 操作擦除。
如果你想自己尝试代码,我在这里放了一个我试过的完全可执行的代码:https://github.com/LoZack19/bare_metal/blob/main/power/power.S
编辑: 在阅读了您的答案后,我认为最好在我的问题中也包括 printh 函数的代码,因为正如您正确指出的那样,错误就在这里,并且 power 函数没有问题:
# printh(%dx)
# dx: hex value to print
# ==> converts the value stored in dx to ascii and prints it
printh:
pusha
mov [=12=]x05, %cx
loop_printh:
cmp [=12=]x00, %dx
je end_loop_printh
call convert # Converts the lower nibble (4-bit) of dx into an ascii character and stores it in HEX_OUT
dec %cx
shr [=12=]x04, %dx
jmp loop_printh
end_loop_printh:
mov $HEX_OUT, %si
call printf
popa
ret
# [...]
HEX_OUT: .asciz "0x0000"
power 函数在这种情况下就很好(除了评论中提到的不相关的错误)。当使用 bx = 10
和 cx = 1
调用时,它会正确地将 10
留在 bx
中。 Single-stepping 在调试器中进行测试是一个很好的测试方法,我发现 Bochs 是一个比 qemu 更方便调试 16 位代码的模拟器。
错误出在您的“坚决排除”打印函数中,特别是在 printh
。考虑当 dx
包含小于或等于 0xf
的值时会发生什么。 loop_printh
的第一次迭代后,低半字节已移出 dx
,因此 dx
现在为 0。因此 loop_printh
处的测试将退出循环第二次迭代。所以高 3 个半字节永远不会被写入 HEX_OUT
,因此它仍然包含任何字符作为前一个打印的高半字节留下的任何字符,这里是 006
.
无论输入如何,您的 printh
都需要将循环迭代四次,以确保为所有高半字节写入 0
。因此,您对循环的测试可能希望基于计数器 cx
的值。
how can my computer remember a value (such as the 0x60 from the first power call) I erased through a mov or a xor operation.
因为你从未真正删除过它:-)