匯編系列其實也在一直更新,只不過更新的頻率會挺慢的。。。由于白天一直忙于工作,空閑時間還要看書、學習各種技術棧,早上也要抽時間早期健身,晚上回家還要陪家人 + 學習,時間安排的滿滿當當,所以我就慢慢寫,各位讀者也別太著急,我其實真想再分一個自己出來。
之前的文章中介紹過 [0] 表示的是內存單元,它一般存儲在 ds 寄存器中,偏移地址為 0 。比如下面的指令
mov ax,[0]
就是將一個內存單元的內容送入 ax,這個內存單元的長度為 2 個字節,正好存放一個字型數據,偏移地址為 0 ,段地址在 ds 中。這種尋址方式相當于是直接尋址。
比如下面代碼
mov al,[0]
就是將一個內存單元的地址送入 al 中,這個內存單元的長度是 1 字節,存放字節型數據,偏移地址為0 ,段地址在 ds 中。
所以要描述一個完整的一個內存單元,應該需要兩種信息:即內存單元的地址和內存單元的長度。
比如我們要讀取一個 10000H 的數據,你可能會需要下面這段代碼。
mov bx,10000H
mov ds,bx
mov al,[0]
上面這三條指令就把 10000H 讀取到了 al 中。
但是表示內存地址的方式不只有直接指定其內存地址,還可以用一種間接尋址的方式,比如 [bx],它表示的是一種寄存器間接尋址,也是一種偏移地址,同樣的,比如我們要讀取一個 10000H 的數據,使用 [bx] 這種方式的代碼如下(假設 ds = 1000H)
mov bx,1
mov ax,[bx]
這樣計算機就會尋找段地址為 1000H,偏移地址為 0001H 的數據放入到 ax 中。
它的中文解釋就是 把 [bx] 指向的地址中的內容,送入 ax 寄存器中。
比如下面這段代碼
mov ax,[bx]
它表示的就是將偏移地址為 bx 的數據,送入到 ax 中,送入的內存單元地址是 2 個字節,存放字型數據。
又比如下面這段代碼
mov al,[bx]
它表示的就是將偏移地址為 bx 的數據,送入到 al 中,送入的內存單元地址是 1 個字節,存放字節型數據。
[bx] 這種間接尋址的好處就是每次偏移地址不是固定的,這為我們接下來的循環指令奠定了基礎。
為了更方便描述后面,我們后面使用 ()
來表示一個寄存器或者內存單元中的內容。
這里需要注意一下,() 內的表示的元素一般有三種類型:
- 寄存器名,比如 (ax) 就表示 ax 中的內容,(al) 就表示 al 中的內容。
- 段寄存器名,比如 (ds) 就表示段寄存器 ds 中的內容。
- 內存單元的物理地址,比如 ((ds) * 16 + (bx)),一個 20 位的數據。
我們知道,寄存器存儲的數據類型有兩種,字型和字節型,字型數據一般用 ax 這類寄存器來存儲,字節型數據一般用 ah 、al 這種寄存器來存儲。
同樣的,() 內的數據類型也有兩種,字型和字節型。比如 (al)、(bl)、(cl) 這種表示的數據就是字節型,而 (ax)、(bx)、(cx) 表示的數據就是字型。
在了解完上述的這些知識點后,我們就可以來正式看一下 [bx] 了。
[BX]
再來啰嗦一下 [bx] 的尋址方式,比如下面代碼
mov ax,[bx]
bx 中存放的數據作為一個偏移地址,這里用 EA 表示(沒有其他意思,只是單純地表示偏移地址),段地址在 ds 中,用 SA 表示(同 EA 的解釋),將 SA:EA 處的數據送入 ax 中,即 (ax) = ((ds) * 16 + (bx))。
可以將內存單元送入寄存器中,也可以將寄存器的數據送入到內存單元中,如下代碼所示
mov [bx],ax
就是將 ax 中的數據送入到 SA:EA 處,即 ((ds) * 16 + (bx)) = (ax)。
為了讓大家加深對 [bx] 的認識,我們通過一些匯編指令來認識一下程序的執行過程,代碼如下
mov ax,2000H
mov ds,ax
mov bx,1000H
mov ax,[bx]
inc bx
inc bx
mov [bx],ax
inc bx
inc bx
mov [bx],ax
inc bx
mov [bx],al
inc bx
mov [bx],al
下面我們就按照每一行指令來分析一下
首先,mov ax,2000H 就是將 2000 送入 ax 中,mov ds,ax 就是將設置段地址為 2000 H,mov bx,1000H 就是將 1000 送入 bx 中,mov ax,[bx] 就是將 2000:1000 處的地址送入到 ax 中(因為段基址為 2000,偏移地址 dx 為 1000),2000H:1000H 處的指令是 00be,所以 ax = 00BEH ,存儲字型數據,示意圖如下
inc bx 就是將寄存器 bx 的值加 1,此處有兩條 inc 指令,所以執行完成后 bx = 1002H,此處段基址:偏移地址為 2000H:1002H。
然后下面 (第七行指令)mov [bx],ax 就是將 ax 中的數據送入到 [bx] 中,也就是 1002H 處,指令執行后,2000:1002 單元的內容為 BE,2000:1003 單元的內容為 00,存放字型數據,執行完成后的示意圖如下
繼續執行第 8、9 行的指令,inc bx ,執行完成后 bx = 1004H,然后執行第 10 行指令 mov [bx],ax ,指令執行前:ds = 2000H,bx = 1004H,mov [bx],ax 相當于是把 ax 中的數據送到 2000:1004 處,指令執行完成后,2000:1004 的單元內容為 BE,2000:1005 的單元內容為 00 ,如下示意圖所示
接下來執行第 11 行指令,inc bx,執行完成后 bx = 1005H,mov [bx],al 是把 al 中的數據送入內存 2000:1005 處,指令執行完成后,2000:1005 處的單元內容為 BE,如下示意圖所示
繼續執行指令,第13、14 行指令和 11 、12 行指令一樣,它的意思就是將 bx 的值加1之后,將 al 的值送入到指定地址處,執行完成后的 ds = 2000H,bx = 1006H,所以 2000:1006 處的內容是 BE(al 存儲的數據),示意圖如下
想必大家跟完上面的流程后,應該對 [bx] 這個間接尋址方式有了比較深刻的認識。
下面想個問題,使用匯編編程計算 2 * 2 ,并將結果存儲在 ax 寄存器中。
這個思路還是比較簡單的,直接將 2 放在 ax 寄存器中,然后執行 ax 的 add 操作就可以了,下面是匯編代碼
assume cs:codesg
codesg segment
mov ax,2
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
上面這段代碼中的計算量還比較低,但是如果要讓你計算 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 呢,你難道要寫 n 個 add ax,ax 嗎?
assume cs:codesg
codesg segment
mov ax,2
add ax,ax
add ax,ax
add ax,ax
add ax,ax
。。。
mov ax,4c00h
int 21h
codesg ends
end
這就很繁瑣啊,所以不能這么玩,那該怎么搞呢?這里就需要一種能夠循環執行add ax,ax 的指令了,這個指令就是 Loop
。
Loop 指令
Loop 指令能夠循環判斷是否執行指定的指令,它的執行流程就相當于我們 Java 中的 for 循環。
我們先來使用 Loop 改寫一下上面 n 個 2 相乘的代碼,然后再講解一下 Loop 的使用。
assume cs:codesg
codesg segment
mov ax,2
mov cx,8
s: add ax,ax
loop s
mov ax,4c00h
int 21h
codesg ends
end
可以看到,我們使用 8 個 2 相乘的代碼被優化的這么簡單,這就是 loop 指令的精髓所在。
其實關鍵代碼就是三條指令,即
- mov cx,8
- s: add ax,ax
- loop s
翻譯過來的意思就是將 8 放在 cx 中,然后給 add ax,ax 處設置一個標號,然后執行 s 循環。
loop 指令的格式是:loop 標號,CPU 執行 loop 指令的時候,要進行兩步操作,第一步:(cx) = (cx) - 1,第二步:判斷 cx 的值,不為 0 則轉至標號(上面代碼是 s)處繼續執行指令,如果為 0 則向下執行(上面代碼中向下繼續執行就是 mov ax,4c00h)。上面代碼中,我們把 8 送入了 cx 中,也就是說,cx 中存儲的就是執行次數。
下面我們詳細介紹一下上面這段程序的執行過程,從中體會一下 cx 和 loop s 是如何配合實現循環的。
(1) 執行 cx,8 ,設置 cx = 8
(2) 執行 add ax,ax(第 1 次)
(3) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 7,(cx) != 0 ,所以轉至 s 處
(4) 執行 add ax,ax(第 2 次)
(5) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 6,(cx) != 0 ,所以轉至 s 處
(6) 執行 add ax,ax(第 3 次)
(7) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 5,(cx) != 0 ,所以轉至 s 處
(8) 執行 add ax,ax(第 4 次)
(9) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 4,(cx) != 0 ,所以轉至 s 處
(10) 執行 add ax,ax(第 5 次)
(11) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 3,(cx) != 0 ,所以轉至 s 處
(12) 執行 add ax,ax(第 6 次)
(13) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 2,(cx) != 0 ,所以轉至 s 處
(14) 執行 add ax,ax(第 7 次)
(15) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 1,(cx) != 0 ,所以轉至 s 處
(16) 執行 add ax,ax(第 8 次)
(15) 執行 loop s 將 cx 的值 - 1,此時 (cx) = 0,(cx) == 0 ,所以轉至 s 處
(16) 執行 mov ax,4c00h(循環結束)
從上面這個過程中,我們可以總結出用 cx 和 loop 指令相配合實現循環功能的 3 點注意事項:
- 在 cx 中存放循環次數。
- loop 指令中的標號所標識的地址要在前面
- 要循環執行的程序段,要寫在標號和 loop 指令的中間。
所以綜上所述,使用 Loop 和 cx 相配合實現的循環功能的結構如下:
mov cx,循環次數
s:
循環執行的程序段
loop s
比如我們想用 Loop 循環計算出 123 * 456 這個值,就可以使用這種方式
assume cs:codesg
codesg segment
mov ax,0
mov cx,456
s:add ax,123
loop s
mov ax,4c00h
int 21h
codesg ends
end
審核編輯 :李倩
-
寄存器
+關注
關注
31文章
5325瀏覽量
120052 -
匯編
+關注
關注
2文章
214瀏覽量
25905
原文標題:原來匯編中的循環是這么玩兒的
文章出處:【微信號:cxuangoodjob,微信公眾號:程序員cxuan】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論