之前的文章中介紹過 [0] 表示的是偏移地址為 0 的內(nèi)存單元 。比如下面的指令
mov ax,[0]
就是將一個(gè)內(nèi)存單元的內(nèi)容送入 ax,這個(gè)內(nèi)存單元的長度為 2 個(gè)字節(jié),是一個(gè)字型數(shù)據(jù),偏移地址為 0 ,段地址在 ds 中,也就是這個(gè)內(nèi)存單元的地址是 ds:0 ,它的物理地址是 (ds * 16 + 0)H。
除了可以傳輸字型數(shù)據(jù),還可以傳輸字節(jié)型數(shù)據(jù),比如下面代碼
mov al,[0]
就是將一個(gè)內(nèi)存單元的地址送入 al 中,這個(gè)內(nèi)存單元的長度是 1 字節(jié),存放字節(jié)型數(shù)據(jù),偏移地址為 0 ,段地址在 ds 中,大白話說也就是將 ds:0 處的一個(gè)字節(jié)傳入到 al 中。
從上面兩個(gè)例子可以看出,假如要描述一個(gè)完整的一個(gè)內(nèi)存單元,應(yīng)該需要兩種信息:即內(nèi)存單元的地址和內(nèi)存單元的長度。
比如我們要讀取一個(gè) 10000H 的數(shù)據(jù),你可能會(huì)需要下面這段代碼。
mov bx,1000H #將 1000H 放入 bx 寄存器中
mov ds,bx #將段寄存器 ds 的值設(shè)為 1000H mov al,[0]#從內(nèi)存單元 1000:0 處讀出1字節(jié)內(nèi)容放入 al 中。
但是表示內(nèi)存地址的方式除了能夠直接指定其內(nèi)存地址之外,還可以用一種間接尋址的方式,這就是 [bx],它表示的是一種寄存器間接尋址,也是一種偏移地址,同樣的,比如我們要讀取一個(gè)物理地址為 10001H 處的數(shù)據(jù),使用 [bx] 這種方式的代碼如下
mov ax,1000H
mov ds,ax mov bx,1 mov ax,[bx]
這樣計(jì)算機(jī)就會(huì)尋找段地址為 1000H,偏移地址為 0001H 的數(shù)據(jù)放入到 ax 中。
它的中文解釋就是 把 [bx] 指向的地址中的內(nèi)容,送入 ax 寄存器中。
比如下面這段代碼
mov ax,[bx]
它表示的就是將偏移地址為 bx 的數(shù)據(jù),送入到 ax 中,送入的是 2 個(gè)字節(jié),也就是字型數(shù)據(jù)。
又比如下面這段代碼
mov al,[bx]
它表示的就是將偏移地址為 bx 的數(shù)據(jù),送入到 al 中,送入的內(nèi)存單元地址是 1 個(gè)字節(jié)的字節(jié)型數(shù)據(jù)。
[bx] 這種間接尋址的好處就是每次偏移地址不是固定的,這為我們接下來的循環(huán)指令奠定了基礎(chǔ)。
( ) 表示法
為了更方便的描述后面,我們使用 () 來表示一個(gè)寄存器或者內(nèi)存單元中的內(nèi)容。
這里需要注意一下,( ) 內(nèi)的能夠表示的內(nèi)容一般有三種類型:
寄存器名,比如 (ax) 就表示 ax 中的內(nèi)容,(al) 就表示 al 中的內(nèi)容。
段寄存器名,比如 (ds) 就表示段寄存器 ds 中的內(nèi)容。
內(nèi)存單元的物理地址,比如 ((ds) * 16 + (bx)),一個(gè) 20 位的數(shù)據(jù)。
我們知道,寄存器存儲(chǔ)的數(shù)據(jù)類型有兩種,字型和字節(jié)型,字型數(shù)據(jù)一般用 ax 這類寄存器來存儲(chǔ),字節(jié)型數(shù)據(jù)一般用 ah 、al 這種寄存器來存儲(chǔ)。
同樣的,( ) 內(nèi)的數(shù)據(jù)類型也有兩種,字型和字節(jié)型。比如 (al)、(bl)、(cl) 這種表示的數(shù)據(jù)就是字節(jié)型,而 (ax)、(bx)、(cx) 表示的數(shù)據(jù)就是字型。下面是幾類 ( ) 的一些用法:
ax 中的內(nèi)容為 0020H:(ax) = 0020H;
2000:1000 處的內(nèi)容為 0010H:(21000H) = 0010H;
mov ax,[2] 則表示為:ax = ((ds * 16) + 2);
mov [2],ax 則表示為:((ds) * 16 + 2) = (ax);
add ax,2 表示為:(ax) = (ax) + 2;
push ax 表示為:(sp) = (sp) - 2, ((ss * 16) + sp) = (ax);
pop ax 表示為:(ax) = ((ss) * 16) + sp), (sp) = (sp) + 2;
idata
idata 表示的就是立即數(shù),這個(gè)概念就更簡(jiǎn)單了,立即數(shù)顧名思義就是直接的數(shù)字,也就是常量。比如 mov ax,[0] ,其中的 0 就是立即數(shù),即 idata = 0 ,所以 [立即數(shù)] = [idata],所以以后我們通常使用 idata 來表示常量,比如下面幾個(gè)例子
mov ax,[idata] 可以表示為 mov ax,[1]mov ax,[2]mov ax,[3]
mov ax,idata 可以表示為 mov ax,1 ? ?mov ax,2 ? ?mov ax,3
知道上面是 啥意思了吧?
[BX]
再來啰嗦一下 [bx] 的尋址方式,比如下面代碼
mov ax,[bx]
bx 中存放的數(shù)據(jù)作為一個(gè)偏移地址,這里用 EA 表示(沒有其他意思,只是單純地表示偏移地址),段地址在 ds 中,用 SA 表示(同 EA 的解釋),將 SA:EA 處的數(shù)據(jù)送入 ax 中,即 (ax) = ((ds) * 16 + (bx))。
可以將內(nèi)存單元送入寄存器中,也可以將寄存器的數(shù)據(jù)送入到內(nèi)存單元中,如下代碼所示
mov [bx],ax
就是將 ax 中的數(shù)據(jù)送入到 SA:EA 處,即 ((ds) * 16 + (bx)) = (ax)。
為了讓大家加深對(duì) [bx] 的認(rèn)識(shí),我們通過一些匯編指令來認(rèn)識(shí)一下程序的執(zhí)行過程,代碼如下
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
初始內(nèi)存示意圖:
下面我們就按照每一行指令來分析一下
首先,mov ax,2000H 就是將 2000 送入 ax 中,mov ds,ax 就是將設(shè)置段地址為 2000 H,mov bx,1000H 就是將 1000 送入 bx 中,mov ax,[bx] 就是將 2000:1000 處的地址送入到 ax 中(因?yàn)槎位窞?2000,偏移地址 dx 為 1000),2000H:1000H 處的指令是 00be,所以 ax = 00BEH ,存儲(chǔ)字型數(shù)據(jù)。
inc bx 就是將寄存器 bx 的值加 1,此處有兩條 inc 指令,所以執(zhí)行完成后 bx = 1002H,此處段基址:偏移地址為 2000H:1002H。
然后下面 (第七行指令)mov [bx],ax 就是將 ax 中的數(shù)據(jù)送入到 [bx] 中,也就是 1002H 處,指令執(zhí)行后,2000:1002 單元的內(nèi)容為 BE,2000:1003 單元的內(nèi)容為 00,存放字型數(shù)據(jù),執(zhí)行完成后的示意圖如下
繼續(xù)執(zhí)行第 8、9 行的指令,inc bx ,執(zhí)行完成后 bx = 1004H,然后執(zhí)行第 10 行指令 mov [bx],ax ,指令執(zhí)行前:ds = 2000H,bx = 1004H,mov [bx],ax 相當(dāng)于是把 ax 中的數(shù)據(jù)送到 2000:1004 處,指令執(zhí)行完成后,2000:1004 的單元內(nèi)容為 BE ,如下示意圖所示
接下來執(zhí)行第 11 行指令,inc bx,執(zhí)行完成后 bx = 1005H,mov [bx],al 是把 al 中的數(shù)據(jù)送入內(nèi)存 2000:1005 處,指令執(zhí)行完成后,2000:1005 處的單元內(nèi)容為 BE,如下示意圖所示
繼續(xù)執(zhí)行指令,第13、14 行指令和 11 、12 行指令一樣,它的意思就是將 bx 的值加一之后,將 al 的值送入到指定地址處,執(zhí)行完成后的 ds = 2000H,bx = 1006H,所以 2000:1006 處的內(nèi)容是 BE(al 存儲(chǔ)的數(shù)據(jù)),示意圖如下
想必大家跟完上面的流程后,應(yīng)該對(duì) [bx] 這個(gè)間接尋址方式有了比較深刻的認(rèn)識(shí)。
下面想個(gè)問題,使用匯編編程計(jì)算 2 * 2 ,并將結(jié)果存儲(chǔ)在 ax 寄存器中。
這個(gè)思路還是比較簡(jiǎn)單的,直接將 2 放在 ax 寄存器中,然后執(zhí)行 ax 的 add 操作就可以了,下面是匯編代碼
assume cs:codesg codesg segment mov ax,2 add ax,ax mov ax,4c00h int 21h codesg ends end
上面這段代碼中的計(jì)算量還比較低,但是如果要讓你計(jì)算 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 呢,你難道要寫 n 個(gè) 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
這就很繁瑣啊,所以不能這么玩,那該怎么搞呢?這里就需要一種能夠循環(huán)之星 add ax,ax 的指令了,這個(gè)指令就是 Loop。
Loop 指令
Loop 指令能夠循環(huán)判斷是否執(zhí)行指定的指令,它的執(zhí)行流程就相當(dāng)于我們 Java 中的 for 循環(huán)。
我們先來使用 Loop 改寫一下上面 n 個(gè) 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 個(gè) 2 相乘的代碼被優(yōu)化的這么簡(jiǎn)單,這就是 loop 指令的精髓所在。
其實(shí)關(guān)鍵代碼就是三條指令,即
mov cx,8
s: add ax,ax
loop s
翻譯過來的意思就是將 8 放在 cx 中,然后給 add ax,ax 處設(shè)置一個(gè)標(biāo)號(hào),然后執(zhí)行 s 循環(huán)。
loop 指令的格式是:loop 標(biāo)號(hào),CPU 執(zhí)行 loop 指令的時(shí)候,要進(jìn)行兩步操作,第一步:(cx) = (cx) - 1,第二步:判斷 cx 的值,不為 0 則轉(zhuǎn)至標(biāo)號(hào)(上面代碼是 s)處繼續(xù)執(zhí)行指令,如果為 0 則向下執(zhí)行(上面代碼中鄉(xiāng)下繼續(xù)執(zhí)行就是 mov ax,4c00h)。上面代碼中,我們把 8 送入了 cx 中,也就是說,cx 中存儲(chǔ)的就是執(zhí)行次數(shù)。
下面我們?cè)敿?xì)介紹一下上面這段程序的執(zhí)行過程,從中體會(huì)一下 cx 和 loop s 是如何配合實(shí)現(xiàn)循環(huán)的。
(1) 執(zhí)行 cx,8 ,設(shè)置 cx = 8
(2) 執(zhí)行 add ax,ax(第 1 次)
(3) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 7,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(4) 執(zhí)行 add ax,ax(第 2 次)
(5) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 6,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(6) 執(zhí)行 add ax,ax(第 3 次)
(7) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 5,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(8) 執(zhí)行 add ax,ax(第 4 次)
(9) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 4,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(10) 執(zhí)行 add ax,ax(第 5 次)
(11) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 3,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(12) 執(zhí)行 add ax,ax(第 6 次)
(13) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 2,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(14) 執(zhí)行 add ax,ax(第 7 次)
(15) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 1,(cx) != 0 ,所以轉(zhuǎn)至 s 處
(16) 執(zhí)行 add ax,ax(第 8 次)
(15) 執(zhí)行 loop s 將 cx 的值 - 1,此時(shí) (cx) = 0,(cx) == 0 ,所以轉(zhuǎn)至 s 處
(16) 執(zhí)行 mov ax,4c00h(循環(huán)結(jié)束)
從上面這個(gè)過程中,我們可以總結(jié)處用 cx 和 loop 指令相配合實(shí)現(xiàn)循環(huán)功能的 3 點(diǎn)注意事項(xiàng):
在 cx 中存放循環(huán)次數(shù)。
loop 指令中的標(biāo)號(hào)所標(biāo)識(shí)的地址要在前面
要循環(huán)執(zhí)行的程序段,要寫在標(biāo)號(hào)和 loop 指令的中間。
所以綜上所述,使用 Loop 和 cx 相配合實(shí)現(xiàn)的循環(huán)功能的結(jié)構(gòu)如下:
mov cx,循環(huán)次數(shù)
s: 循環(huán)執(zhí)行的程序段 loop s
比如我們想用 Loop 循環(huán)計(jì)算出 123 * 456 這個(gè)值,就可以使用這種方式
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
編輯:黃飛
?
評(píng)論
查看更多