汇编循环遍历寄存器值的每一位
Assembly Loop Through Each Bit of Register Value
我有一个寄存器 $t0,其中存储了一些整数。例如,假设我将整数 1100 存储到其中。这个值的二进制表示是 0000010001001100。当然对于 32 位寄存器它可以扩展到 32 位,但这很容易做到。
我正在尝试在程序集中实现一个循环,循环遍历寄存器值的每一位并检查它是 1 还是 0。该怎么做?
也许我误解了寄存器的本质。据我了解,寄存器存储一个 32 位数字,是吗?这是否意味着每一位都存储在特定地址?
我试过对寄存器使用移位并检查位,但失败了。我还查看了 lb 命令,但这会加载字节,而不是位。那么一个人会做什么?
在任何处理器上,设置一个循环来计算寄存器中的位数,在本例中为 32。在每次通过循环时,将感兴趣的寄存器加 1。然后将结果添加到累加器,最后移位寄存器。这给了你设置位数。
具体指令因处理器而异。要循环,您通常会设置一个标签,递减一个计数器,然后执行一个名称类似于 branch_not_equal_to 零(BNZ、BNEQ0 之类的名称)的指令。 and 的名称类似于 AND、ANDI(和立即数)。 ADD 可能是 ADD、ADC(带进位加法)。移位类似于 ASR(算术右移)LSR(逻辑右移),您可能必须将其传递 1 才能表示仅移位一个位置。但是所有处理器都允许您以这种方式读出寄存器位。
一些基础知识:
大多数(全部?)移位指令将位移出进位标志
大多数(全部?)CPU 有一个分支命令,跳转到设置了进位标志的位置
将此结合起来,您可以执行以下操作:
load register1,< the value to be tested >
load register2, 0
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lskip
increase register2
Lskip:
jump Lrepeat
Lexit: ; when you end up here, your register2
; holds the count of bit register1 had set
还有一些优化可以做:
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lskip <-- note: here you jump ...
increase register2
Lskip:
jump Lrepeat <-- ... to jump again!
Lexit:
=====>
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lrepeat <-- so you can optimize it this way
increase register2
Lskip:
jump Lrepeat
Lexit:
一些 CPU 有一个 "add carry" 指令,
例如6502:
ADC register,value ; register = register + value + Carry flag
这可以用来避免分支(条件跳转),方法是在每个循环中将 0(当然加上进位)添加到 register2
shift right register1
add with carry register2,0 ; may look weird, but this adds a
; 1 if the carry is set, which is
; exactly what we want here
jump Lrepeat
请注意,您不需要知道寄存器大小!您只需循环直到寄存器为 1,这可以为您节省很多时间,例如如果您的值类似于 0000 0000 0000 0000 0000 0000 0001 1001
将要迭代的寄存器记为R0。因此,例如,最低有效位是 R0= 1011 0101
.
2) 使用第二个清除寄存器R1=0000 0001
.
3) AND
R1
with R0
然后右移 R0
(因此下一次迭代检查 R0
的下一位)。
4) 设 R3
为第三个递增 1 IF
的寄存器 AND
运算结果为 1(即,您 运行 为 1在 R0
)。 ELSE
,再次循环检查R0
.
中的下一位
您可以通过递减循环计数器迭代整个 32 位或您选择的长度。
我有一个寄存器 $t0,其中存储了一些整数。例如,假设我将整数 1100 存储到其中。这个值的二进制表示是 0000010001001100。当然对于 32 位寄存器它可以扩展到 32 位,但这很容易做到。
我正在尝试在程序集中实现一个循环,循环遍历寄存器值的每一位并检查它是 1 还是 0。该怎么做?
也许我误解了寄存器的本质。据我了解,寄存器存储一个 32 位数字,是吗?这是否意味着每一位都存储在特定地址?
我试过对寄存器使用移位并检查位,但失败了。我还查看了 lb 命令,但这会加载字节,而不是位。那么一个人会做什么?
在任何处理器上,设置一个循环来计算寄存器中的位数,在本例中为 32。在每次通过循环时,将感兴趣的寄存器加 1。然后将结果添加到累加器,最后移位寄存器。这给了你设置位数。
具体指令因处理器而异。要循环,您通常会设置一个标签,递减一个计数器,然后执行一个名称类似于 branch_not_equal_to 零(BNZ、BNEQ0 之类的名称)的指令。 and 的名称类似于 AND、ANDI(和立即数)。 ADD 可能是 ADD、ADC(带进位加法)。移位类似于 ASR(算术右移)LSR(逻辑右移),您可能必须将其传递 1 才能表示仅移位一个位置。但是所有处理器都允许您以这种方式读出寄存器位。
一些基础知识:
大多数(全部?)移位指令将位移出进位标志
大多数(全部?)CPU 有一个分支命令,跳转到设置了进位标志的位置
将此结合起来,您可以执行以下操作:
load register1,< the value to be tested >
load register2, 0
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lskip
increase register2
Lskip:
jump Lrepeat
Lexit: ; when you end up here, your register2
; holds the count of bit register1 had set
还有一些优化可以做:
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lskip <-- note: here you jump ...
increase register2
Lskip:
jump Lrepeat <-- ... to jump again!
Lexit:
=====>
Lrepeat:
compare register1 with 0
jump if zero Lexit
shift right register1
jump no carry Lrepeat <-- so you can optimize it this way
increase register2
Lskip:
jump Lrepeat
Lexit:
一些 CPU 有一个 "add carry" 指令, 例如6502:
ADC register,value ; register = register + value + Carry flag
这可以用来避免分支(条件跳转),方法是在每个循环中将 0(当然加上进位)添加到 register2
shift right register1
add with carry register2,0 ; may look weird, but this adds a
; 1 if the carry is set, which is
; exactly what we want here
jump Lrepeat
请注意,您不需要知道寄存器大小!您只需循环直到寄存器为 1,这可以为您节省很多时间,例如如果您的值类似于 0000 0000 0000 0000 0000 0000 0001 1001
将要迭代的寄存器记为R0。因此,例如,最低有效位是 R0= 1011 0101
.
2) 使用第二个清除寄存器R1=0000 0001
.
3) AND
R1
with R0
然后右移 R0
(因此下一次迭代检查 R0
的下一位)。
4) 设 R3
为第三个递增 1 IF
的寄存器 AND
运算结果为 1(即,您 运行 为 1在 R0
)。 ELSE
,再次循环检查R0
.
您可以通过递减循环计数器迭代整个 32 位或您选择的长度。