在js中双重循环,代码如下:
for(let i =0; i < l1;i++){for(let j=0; j< l2;j++){// todo...}
}
以上代码在汇编中是如何实现的呢.
mov cx, 4
s: mov ax,1loop s
assume cs:codesg, ds:datasgdatasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg endscodesg segment
start: mov ax, datasgmov ds, axmov bx, 0 ; 用BX来定位行mov cx, 4s0: mov si, 0 ; 用si来定位列mov cx, 3s: mov al, [bx+si]and al, 11011111bmov [bx+si], alinc siloop s ; 此时的 cx 已经为0add bx, 16loop s0 ; cx = cx -1 , 再判断 cx 为是否为0mov ax, 4c00hint 21hend codesg
end start
原因如下: 每当执行 loop 语句时, 实际是执行 cx = cx -1 然后判断 cx 是否为0. 于是在内层循环后(loop s), cx =0 , 然后再到 loop s0时,此时先执行 cx = cx - 1, 即此时 cx = FFFF 因此会陷入死循环 改进办法.用寄存器dx来保存进入内层循环的cx,然后再内存循环结束时,将寄存器dx中的值赋给cx
start: mov ax, datasgmov ds, axmov bx, 0mov cx, 4s0: mov dx, cxmov si, 0mov cx, 3s: mov al, [bx+si]and al, 11011111bmov [bx+si], alinc siloop sadd bx, 16mov cx, dxloop s0mov ax, 4c00hint 21h
以上方法可以解决两层循环的问题,但是CPU中的寄存器毕竟是有限的,当循环次数多的时候,寄存器将不够用. 考虑到内存,可以将寄存器cx的值存入内存中.然后在内存循环结束后,在从内存中读取值给cx
assme cs:codesg, ds: datasgdatasg segment
; 其他代码略
dw 0 ; 定义一个字单元, 用来保存cx
datasg endscodesg segment
start: mov ax, datasgmov ds, ax ; 汇编中用ds来定位数据地址mov bx, 0 ; 偏移量为0mov cx, 4 ; 外层循环为4s0: mov ds:[40H], cx ; 将外层循环的次数保存到内存datasg:40H单元中mov si, 0mov cx, 3s: mov al, [bx+si]and al, 11011111bmov [bx+si], alinc siloop sadd bx, 16 ; 移到下一个字单元mov cx, ds:[40H] ; 从内存中取出当前循环的次数loop s0mov ax, 4c00hint 21h
end codesg
end start
以上方法可以解决CPU中寄存器不够用的情况,但对于保存的多个数据,程序猿们必须要记住数据保存在哪个内存单元中.当代码量大的时候,需要写很多注释,也不利于阅读与维护 看VC++ 6.0编译器是如何处理的 通过反编译,查看汇编源码可以发现. 在遇到函数时,编译器将当前环境push进一个栈中,当执行完毕,将栈中的环境pop出来 于是上面的代码可以改为如下:
stack segment ; 栈空间dw 0,0,0,0,0,0,0,0 ; 16字节(根据需要定)
stack endscodesg segment
start: mov ax, stacksgmov ss, axmov sp, 16 ; 汇编中ss 指向栈端, sp代表偏移量为16mov ax, datasgmov ds, axmov bx, 0mov cx, 4s0: push cxmov si,0mov cx, 3s: mov al, [bx+si]and al, 11011111bmov [bx+si], alinc siloop sadd bx, 16pop cxloop s0mov ax, 4c00hint 21h
codesg ends
end start