匯編語言之寄存器(內(nèi)存訪問)
1、內(nèi)存中字的存儲(chǔ)
2、ds和【address】
CPU要讀寫一個(gè)內(nèi)存單元的時(shí)候,必須先給出這個(gè)內(nèi)存單元的地址,在8086CPU中,內(nèi)存地址由段地址和偏移地址組成,
其中,段地址存放在段寄存器DS中,DS稱作數(shù)據(jù)段寄存器。
如果我們要將內(nèi)存地址為14A70的內(nèi)存單元中的數(shù)據(jù)送入AL中,可以用如下的程序段進(jìn)行:
Mov bx,1000H ;把段地址1000H送入bx中。
mov ds,bx ;把bx中的數(shù)值送入ds中。
Mov al,[4a70] ;把段地址為1000H,偏移地址位4a70H的內(nèi)存單元中的數(shù)據(jù)送入al中。
[address]表示一個(gè)內(nèi)存單元,內(nèi)存單元中的address表示偏移地址,它的中文翻譯就是地址的意思。
內(nèi)存單元[address],我們可以知道它的地址是多少,但它的數(shù)值是多少,我們并不知道。
3、mov指令
傳送指令mov可以完成以下4種傳送:
1:將數(shù)據(jù)直接送入寄存器。 如:mov ax,1230H。
2:將一個(gè)寄存器中的數(shù)據(jù)送入另一個(gè)寄存器。 如:mov ax,bx
3:將一個(gè)內(nèi)存單元中的數(shù)據(jù)送入一個(gè)寄存器。 如:mov ax,[27b0]
4:將一個(gè)寄存器中的數(shù)據(jù)送入內(nèi)存單元中。 如:mov [607c],bx
注意:不能直接用數(shù)據(jù)送給段寄存器,如:mov ds,1230H 是錯(cuò)誤的,必須要用一個(gè)寄存器來進(jìn)行中轉(zhuǎn)。比如:
Mov bx,1230H
Mov ds,bx
這樣就行了。為什么8086CPU不支持將數(shù)據(jù)直接送入段寄存器呢?這屬于8086CPU硬件設(shè)計(jì)的問題,我們只要知道這一
點(diǎn)就行了。
4、add和sub指令
Mov指令有兩個(gè)操作對(duì)象,add和sub同樣有兩個(gè)操作對(duì)象。
Add為加法指令,格式:add 操作對(duì)象1,操作對(duì)象2。功能:兩數(shù)相加,并把結(jié)果保存到操作對(duì)象1中。
Add有以下幾種形式:
Add 寄存器,數(shù)據(jù) 如:add ax, 8
Add 寄存器,寄存器 如:add ax, bx
Add 寄存器,內(nèi)存單元 如:add ax, [27a0]
Add 內(nèi)存單元,寄存器 如:add [46e9], bx
Sub為減法指令,格式:sub 操作對(duì)象1,操作對(duì)象2。功能:兩數(shù)相減,即從操作對(duì)象1減去操作對(duì)象2,其結(jié)果保存到
操作對(duì)象1中。
Sub 也有以下幾種形式:
Sub 寄存器,數(shù)據(jù) 如:sub ax, 9
Sub 寄存器,寄存器 如:sub ax, bx
Sub 寄存器,內(nèi)存單元 如:sub ax, [b027]
Sub 內(nèi)存單元,寄存器 如:sub [8601], bx
5、數(shù)據(jù)段
前面講過(參見2.7節(jié)),對(duì)于8086PC機(jī),在編程時(shí),可以根據(jù)需要,將一組內(nèi)存單元定義為一個(gè)段。我們可以將一組長(zhǎng)度
為N(N≤64KB)、地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元當(dāng)作專門存儲(chǔ)數(shù)據(jù)的內(nèi)存空間,從而定義了一個(gè)數(shù)據(jù)段。
比如:用123B0H~123B9H這段內(nèi)存空間來存放數(shù)據(jù),我們就可以認(rèn)為123B0H~123B9H這段內(nèi)存是一個(gè)數(shù)據(jù)段,長(zhǎng)度
為10個(gè)字節(jié)。
如何訪問數(shù)據(jù)段中的數(shù)據(jù)呢?首先用DS存放數(shù)據(jù)段的段地址,然后用相關(guān)的指令訪問數(shù)據(jù)段中的內(nèi)存單元。
比如,將123B0H~123B9H的內(nèi)存單元定義為數(shù)據(jù)段,現(xiàn)在要累加這個(gè)數(shù)據(jù)段中的前3個(gè)單元中的數(shù)據(jù),代碼如下:
Mov ax, 123BH
Mov ds, ax ;將123BH送入ds中,作為數(shù)據(jù)段的段地址。
Mov al, 0 ;用al存放累加結(jié)果,先把a(bǔ)l中的數(shù)據(jù)清零。
Add al, [0] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為0)中的數(shù)值加到al中。
Add al, [1] ;將數(shù)據(jù)段第二個(gè)單元(偏移地址為1)中的數(shù)據(jù)加到al中。
Add al, [2] ;將數(shù)據(jù)段第三個(gè)單元(偏移地址為2)中的數(shù)據(jù)加到al中。
在1.5節(jié)中,我們說過,在內(nèi)存中指令和數(shù)據(jù)沒有任何區(qū)別,都是二進(jìn)制信息,CPU在工作的時(shí)候,把有的信息看作指令,
把有的信息看作數(shù)據(jù),那么CPU在什么時(shí)候把它看作指令?在什么時(shí)候把它看作數(shù)據(jù)呢?在2.5節(jié)中我們回答了第一個(gè)問題,現(xiàn)
在可以回答第二個(gè)問題了。
答:只要把這一段內(nèi)存單元的段地址放到DS中,在用mov、add、sub等訪問內(nèi)存單元的指令時(shí),CPU就會(huì)將這些內(nèi)存單
元看作數(shù)據(jù)來訪問。
6、棧
棧是一種具有特殊的訪問方式的存儲(chǔ)空間,它的特殊性就在于:最后進(jìn)入這個(gè)空間的數(shù)據(jù)最先出去??梢杂靡粋€(gè)盒子和3本書
來描述棧的這種操作方式。
入棧:把A放入棧中,再把B放入棧中,然后再把C放入棧中。就形成了A、B、C
出棧:先出棧頂C,然后是B,然后A,
特點(diǎn):先進(jìn)后出
入棧和出棧。入棧就是將一個(gè)新的元素放到棧頂,出棧就是從棧
頂取出一個(gè)元素。棧頂?shù)脑乜偸亲詈笕霔?,需要出棧時(shí)又最先被從棧中取出,棧的這種操作規(guī)則被稱為L(zhǎng)IFO(last in first out,
后進(jìn)先出)
7、cpu提供的棧機(jī)制
現(xiàn)今的CPU中都有棧的設(shè)計(jì)。8086CPU提供的入棧和出棧指令,最基本的兩個(gè)是push(入棧)和pop(出棧)。比如:
Push ax 表示將寄存器ax中的數(shù)據(jù)送入棧中,pop ax 表示從棧頂取出數(shù)據(jù)送入ax。8086CPU的入棧和出棧操作都是以字為
單位進(jìn)行的。
下面兩張圖描述了push和pop指令的執(zhí)行過程。
上面兩張圖指令的執(zhí)行過程,寫成代碼如下:
Mov ax, 123H
Push ax
Mov bx, 2266H
Push bx
Mov cx, 1122H
Push cx
Pop ax
Pop bx
Pop cx
注意:字型數(shù)據(jù)用兩個(gè)內(nèi)存單元存放,高地址單元存放高8位,低地址單元存放低8位。
看了上面兩張圖后,現(xiàn)在提出兩個(gè)問題。問題1:我們將10000H~1000FH這段內(nèi)存當(dāng)作棧來使用,CPU是如何知道這段空
間是棧?關(guān)于這個(gè)問題將在3.10節(jié)解答。
問題2:push ax等入棧指令執(zhí)行時(shí),要將寄存器中的數(shù)據(jù)放入當(dāng)前棧頂單元的上方,成為新的棧頂元素;pop ax等指令執(zhí)
行時(shí),要從棧頂單元取出數(shù)據(jù)送入寄存器中。顯然,push、pop在執(zhí)行的時(shí)候,CPU必須要知道哪個(gè)單元是棧頂單元,可是,如
何知道?
答:8086CPU中,有兩個(gè)寄存器,堆棧段寄存器SS,堆棧指針寄存器SP,棧頂?shù)亩蔚刂反娣旁赟S中,偏移地址存放在
SP中,任意時(shí)刻,SS:SP指向棧頂元素,push和pop指令在執(zhí)行時(shí),CPU從SS和SP中得到棧頂單元的地址。
現(xiàn)在,我們可以完整地描述push和pop指令的功能了,例如:push ax。push ax的執(zhí)行,由以下兩步完成:
1. SP=SP-2,SS:SP指向當(dāng)前棧頂前面的單元,以當(dāng)前棧頂前面的單元為新的棧頂。
2. 將ax中的數(shù)據(jù)送入SS:SP指向的內(nèi)存單元處,SS:SP此時(shí)指向新棧頂。
下圖描述了push ax的執(zhí)行過程。
將10000H~1000FH這段空間當(dāng)作棧段,SS=1000H,??臻g大小為16字節(jié),棧最底部的字單元地址為1000:000E,任
意時(shí)刻,SS:SP指向棧頂,當(dāng)棧中只有一個(gè)元素的時(shí)候,SS=1000H,SP=000EH,棧為空,就相當(dāng)于棧中唯一的元素出棧后,
SP=SP+2,原來為000EH,加2后SP=0010H,所以,當(dāng)棧為空的時(shí)候,SS=1000H,SP=0010H。
換一個(gè)角度看,任意時(shí)刻,SS:SP指向棧頂元素,當(dāng)棧為空的時(shí)候棧中沒有元素,也就不存在棧頂元素,所以SS:SP只能指
向棧的最底部單元下面的單元,該單元的偏移地址為棧最底部的字單元的偏移地址+2,棧最底部字單元的地址為1000:000E,
所以,棧空時(shí),SP=0010H。
接下來,我們描述pop指令的功能,例如:pop ax。Pop ax的執(zhí)行過程和push ax剛好相反,由以下兩步完成:
1. 將SS:SP指向的內(nèi)存單元處的數(shù)據(jù)送入ax中。
2. SP=SP+2,SS:SP指向當(dāng)前棧頂下面的單元,以當(dāng)前棧頂下面的單元為新的棧頂。
下圖描述了pop ax的執(zhí)行過程。
注意:上圖中,出棧后,SS:SP指向新的棧頂1000EH,pop操作前的棧頂元素,1000CH處的數(shù)據(jù)2266H依然存在,但
是,它已不在棧中,當(dāng)再次執(zhí)行push等入棧指令后,SS:SP移至1000CH,并在里面寫入新的數(shù)據(jù),將它覆蓋。
8、push指令 pop指令
Push和pop指令的格式有如下兩種形式:
第一種形式:push 寄存器 ;將一個(gè)寄存器中的數(shù)據(jù)入棧。
Pop 寄存器 ;出棧,用一個(gè)寄存器接收出棧的數(shù)據(jù)。
這一種形式,它們可以在棧和寄存器之間傳送數(shù)據(jù)。
注意:上面的寄存器可以是段寄存器,比如,可以是:push ds、pop ds。
第二種形式:push 內(nèi)存單元 ;將一個(gè)內(nèi)存單元中的字型數(shù)據(jù)入棧(注意,棧操作都是以字為單位)。
Pop 內(nèi)存單元 ;出棧,用一個(gè)內(nèi)存字單元接收出棧的數(shù)據(jù)。
比如:mov ax, 1000H
Mov ds, ax ;將內(nèi)存單元的段地址放在DS中。
Push [2a38] ;將內(nèi)存單元[2a38]中的字型數(shù)據(jù)入棧。
Pop [2a3a] ;出棧,出棧的數(shù)據(jù)送入內(nèi)存單元[2a3a]。
這一種形式,它們可以在棧和內(nèi)存單元之間傳送數(shù)據(jù)。
指令執(zhí)行時(shí),CPU要知道內(nèi)存單元的地址,可以在push、pop指令中給出內(nèi)存單元的偏移地址,段地址在指令執(zhí)行時(shí),CPU
從DS中取得。
Push和pop實(shí)質(zhì)上是一種內(nèi)存?zhèn)魉椭噶?,與mov指令不同的是,push和pop指令訪問的??臻g的地址不是在指令中給出
的,而是由SS:SP指出的。同時(shí),push和pop指令還要改變SP中數(shù)值。Mov指令只需一步操作,就是傳送,而執(zhí)行push、
Pop指令需要兩步操作,執(zhí)行push時(shí),先改變SP,后向SS:SP處傳送;執(zhí)行pop時(shí),先讀取SS:SP處的數(shù)據(jù),后改變SP。
9 、 棧段
前面講過(參見2.7節(jié)),對(duì)于8086PC機(jī),在編程時(shí),可以根據(jù)需要,將一組內(nèi)存單元定義為一個(gè)段。我們可以將長(zhǎng)度為N
(N≤64KB)的一組地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元,當(dāng)作??臻g來用,從而定義了一個(gè)棧段。
比如,我們將10010H~1001FH這段內(nèi)存空間當(dāng)作棧來用,以棧的方式進(jìn)行訪問,這段空間就可以認(rèn)為是一個(gè)棧段,大小為
16個(gè)字節(jié)。
如何使得如push、pop等棧操作指令訪問我們定義的棧段呢?那就是要將SS:SP指向我們定義的棧段。
現(xiàn)在我們來回答3.8節(jié)中的第一個(gè)問題。答:只要這段內(nèi)存單元被SS:SP指向,那么,CPU就會(huì)把這段空間當(dāng)作棧來使用。
作者:chen.yu
深信服三年半工作經(jīng)驗(yàn),目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號(hào):編程入門到禿頭 或掃描下面二維碼
零基礎(chǔ)入門進(jìn)階人工智能(鏈接)