我是 ARM 汇编语言的新手,我知道如何制作简单的 for 循环,但是,当我尝试将概念应用于嵌套循环时,我对如何设置它感到非常困惑。我想知道是否有一般设置 for 循环的规则?也许我可以使用该规则来创建嵌套循环?
举个例子,如果我有一个像下面这样用 C 编写的简单嵌套循环代码,它在汇编中会是什么样子?非常感谢详细的解释,谢谢!
for(int i = 0; i < a; i++){
for(int j = 0; j < b; j++){
int sum = 0;
for(k = 0; k < c; k++){
for( l = 0; l < d; l++){
int temp1 = i+k;
int temp2 = j+k;
}
}
}
}
答案 0 :(得分:1)
对于两个嵌套循环,我通常会做这样的事情。 *请记住,在此代码中,i
(外循环计数器)将是 R0,而 j
(内循环计数器)将是 R1。而R2用于计算内外循环的循环次数。
AREA myCode, CODE, READONLY
ENTRY
EXPORT __main
__main
MOV R2, #0
MOV R0, #2 ; ** outer loop (loop 1) counter initialization **
LOOP1 CBZ R0, STOP ; if (R0 == 0 && R1 == 0) then branch to somewhere. (I'm just ending program here)
; between outer and inner loop
; could do anything but I'm just icreamenting R2
ADD R2, R2, #1
MOV R1, #3 ; ** inner loop (loop 2) counter initialization **
LOOP2 CBZ R1, CNTULP1
; code inside of inner loop will be here
; could do anything but I'm just increamenting R2
ADD R2, R2, #1
; Notice) when you need to continue loop2 => branch to CNTULP2. else if you wanted to continue loop1 => branch to CNTULP1
; and when you wanted to break loops => branch out of them for example branch to STOP
CNTULP2 ; continue loop2 (inner loop)
SUB R1, R1, #1 ; decreament inner loop counter
B LOOP2
CNTULP1 ; continue loop1 (outer loop)
SUB R0, R0, #1 ; decreament outer loop counter
B LOOP1
STOP B STOP
END
答案 1 :(得分:0)
这与您嵌套它们的单个循环没有什么不同。与 C 或其他语言相同(为每个循环使用不同的变量/寄存器,以便它们可以不受干扰地嵌套)。
something holds i (register or memory)
something holds a (register or memory)
i = 0;
loop0:
get i
get a
compare i and a
if signed greater than or equal jump to loop0_done
do stuff
get i
increment i
jmp to loop0
loop0_done:
你也可以这样设计:
something holds i (register or memory)
something holds a (register or memory)
i = 0;
loop0:
get i
get a
compare i and a
if signed greater than or equal jump to loop0_done
do stuff
get i
increment i
loop0_mid:
get a
compare i and a
if signed less than jump to loop0
让我们假设 a 和 b 作为 r0 和 r1 传入。我们希望这段代码遵循正常的 arm 调用约定。
for(int i = 0; i < a; i++){
for(int j = 0; j < b; j++)
do stuff
}
}
push {r4,r5,r6,r7}
;@ A loop
mov r4,#0 ;@ i
mov r5,r0 ;@ a
mov r7,r1 ;@ b
b a_loop_mid
a_loop:
mov r6,#0 ;@ j
b b_loop_mid
b_loop:
do stuff
add r6,#1
b_loop_mid:
cmp r6,r7
blt b_loop
add r4,#1
a_loop_mid
cmp r4,r5
blt a_loop
pop {r4,r5,r6,r7}
两个循环看起来一样,只是使用了不同的寄存器/变量:
mov r4,#0 ;@ i
mov r5,r0 ;@ a
b a_loop_mid
a_loop:
do stuff
add r4,#1
a_loop_mid
cmp r4,r5
blt a_loop
和
mov r6,#0 ;@ j
b b_loop_mid
b_loop:
do stuff
add r6,#1
b_loop_mid:
cmp r6,r7
blt b_loop
然后将它们嵌套。现在你会用完寄存器,所以有些东西会想在堆栈上,所以访问说 b 可能是
ldr r1,[sp,#12]
并且不需要烧录r7。
同样,for 循环将计数为 n,但循环中未使用计数变量,那么您可以根据指令集保存一条或几条指令。有多个 arm 指令集,而不是计数和执行
add this,#1
cmp this,that
blt somewhere
可以在循环内保存一条指令
subs this,#1
bne somewhere
你需要正确地初始化它,如果
for(i=0;i<a;++)
a 是 5 那么我会数 0,1,2,3,4, 五件事。所以你可以改为数 5,4,3,2,1, 五件事
this=a
label:
...
subs this,#1
bne label
并将该指令保存在循环中
某些 arm 指令集有 cbz 和 cbnz(如果为零或不为零,则比较并分支)
所以
sub this,#1
cbnz label
那里没有真正的节省。一些指令集(不是 arm)有一个递减,如果不是零则跳转
this = a
label:
djnz this, label
保存另一条指令。
如果您使用典型的 arm 调用约定与编译代码链接,那么 r0-r3 是易失性的,而 r4 以上的大部分是非易失性的(您必须保留它们)。这意味着,如果您想在循环内调用另一个函数,那么该函数可能会与 r0-r3 混淆,因此您不能将它们用作跨越调用边界的循环的一部分。
如果 r0 中有 a
mov r2,#0
a_loop:
bl somewhere
cmp r2,r0
blt a_loop
这有两个问题,r2 和 r0 都可以在某处函数中改变,所以比较和东西可能会搞砸。
这样做
mov r2,r0
a_loop:
bl somewhere
subs r2,#1
bne a_loop
只有 r2 被这个循环搞砸了,但是如果在这个可能已经被破坏的代码之后需要 r0 中的 a 值。这就是为什么对于更简单的事情你可能会看到这个
push {r4,...}
mov r4,r0
...
pop {r4,...}
保存调用者 r4,然后在此函数中使用 r4 来保存 a 值。或者最坏的情况
push {r0,...}
ldr rn,[sp,??] read a
pop {r0,...}
sp 偏移量的位置取决于堆栈上有多少东西以及 r0 值的位置。或者使用堆栈框架并从中引用。
归根结底,嵌套循环与任何其他语言没有什么不同。您必须拥有不干扰其他嵌套循环的循环变量。您的循环完全位于外循环中。