概述
本文來介紹最基礎(chǔ)的8086匯編,將通過關(guān)鍵點(diǎn)的突出以及個(gè)人理解,而不會(huì)將原理做重點(diǎn)介紹,爭(zhēng)取最快的速度來學(xué)會(huì)匯編并且使用,我們平時(shí)接觸的C/C++/JAVA等語言都是高級(jí)語言,而計(jì)算機(jī)只能運(yùn)行機(jī)器語言也就是0和1,高級(jí)語言通過編譯成匯編語言再編譯成機(jī)器語言運(yùn)行到計(jì)算機(jī)上。高級(jí)語言、匯編語言、機(jī)器語言特性如下:
- 匯編語言不區(qū)分大小寫
- 匯編語言與機(jī)器語言一一對(duì)應(yīng),每一條機(jī)器語言都有一條匯編語言對(duì)應(yīng)
- 匯編語言可以通過編譯得到機(jī)器語言,機(jī)器語言可以通過反匯編得到匯編語言
- 高級(jí)語言可以通過編譯得到匯編語言或者機(jī)器語言,但匯編語言或者機(jī)器語言幾乎不可能還原成高級(jí)語言
程序執(zhí)行流程:程序被裝載進(jìn)內(nèi)存,CPU會(huì)對(duì)內(nèi)存進(jìn)行讀和寫,根據(jù)讀取到的指令也會(huì)控制硬件設(shè)備比如顯示器,音響,話筒等。我們?cè)趯W(xué)習(xí)匯編過程中絕大部分指令都是跟CPU和內(nèi)存相關(guān)的。
總線
總線(Bus)是計(jì)算機(jī)各種功能部件之間傳送信息的公共通信干線,它是由導(dǎo)線組成的傳輸線束, 按照計(jì)算機(jī)所傳輸?shù)男畔⒎N類,計(jì)算機(jī)的總線可以劃分為數(shù)據(jù)總線、地址總線和控制總線,分別用來傳輸數(shù)據(jù)、數(shù)據(jù)地址和控制信號(hào)。總線是一種內(nèi)部結(jié)構(gòu),它是CPU、內(nèi)存、輸入、輸出設(shè)備傳遞信息的公用通道,主機(jī)的各個(gè)部件通過總線相連接,外部設(shè)備通過相應(yīng)的接口電路再與總線相連接,從而形成了計(jì)算機(jī)硬件系統(tǒng)。在計(jì)算機(jī)系統(tǒng)中,各個(gè)部件之間傳送信息的公共通路叫總線,微型計(jì)算機(jī)是以總線結(jié)構(gòu)來連接各個(gè)功能部件的。
- 地址總線:它的寬度決定了CPU的尋址能力,8086的地址總線寬度是20,它的尋址能力是2^20 =1M,32位CPU尋址能力則是2^32=4G, 64位CPU尋址能力是2^64 = 4G * 4G,64位CPU提供足夠大的內(nèi)存地址,目前實(shí)際沒有用到那么大的地址。
- 數(shù)據(jù)總線:它的寬度決定了CPU的單次數(shù)據(jù)傳送量,也就是數(shù)據(jù)傳送速度,8086的數(shù)據(jù)總線寬度是16,所以單次最大傳遞2個(gè)字節(jié)的數(shù)據(jù),比如8086需要傳送數(shù)據(jù)89D8H,89D8H占兩個(gè)字節(jié)第一次傳送DB,第二次傳送89。
- 控制總線:它的寬度決定了CPU對(duì)其他器件的控制能力、能有多少種控制。
內(nèi)存
所有的內(nèi)存單元都有唯一的地址,叫做物理地址,各類存儲(chǔ)器的物理地址情況,內(nèi)存地址空間的大小受CPU地址總線寬度的限制。8086的地址總線寬度為20,可以定位2^20個(gè)不同的內(nèi)存單元(內(nèi)存地址范圍0x00000~0xFFFFF)所以8086的內(nèi)存空間大小為1MB
- 0x00000~0x9FFFF:主存儲(chǔ)器。可讀可寫
- 0xA0000~0xBFFFF:向顯存中寫入數(shù)據(jù),這些數(shù)據(jù)會(huì)被顯卡輸出到顯示器,可讀可寫
- 0xC0000~0xFFFFF:存儲(chǔ)各種硬件、系統(tǒng)信息。只讀
尋址方式
8086CPU是16位的,但是地址總線是20位的,按理說CPU尋址能力只有2^16=64KB,那如何輸送地址20位呢,這是8086的特殊性,它內(nèi)部采用了地址加法器2個(gè)16位地址合成的方法生成1個(gè)20位的地址,一個(gè)地址稱為段地址,一個(gè)稱為偏移地址,地址加法器采用物理地址=段地址x16+偏移地址,比如8086要訪問123C8H的內(nèi)存單元,此時(shí)8086地址加法器進(jìn)行合成,其步驟如下:
1.相關(guān)部件提供段地址1230和偏移地址00C8給地址加法器。
2.段地址1230和偏移地址00C8送入地址加法器。
3.地址加法器合成屋里地址:1230x16+00C8 = 123C8H。
4.輸出物理地址。
在8086中,一般段地址又稱為這個(gè)段的起始地址,偏移地址決定了這個(gè)段的最大長(zhǎng)度:2^16=64KB。
CPU
CPU典型構(gòu)成:寄存器,運(yùn)算器,控制器。寄存器負(fù)責(zé)信息存儲(chǔ),運(yùn)算器負(fù)責(zé)信息處理,控制器負(fù)責(zé)控制其它器件的工作。對(duì)我們目前學(xué)到的匯編主要是跟寄存器打交道,8086有14個(gè)16位寄存器,可以存放2個(gè)字節(jié),8086寄存器如下所示:
1.CPU首先會(huì)將紅色內(nèi)存空間的值放到AX寄存器中:mov ax,地址1
2.然后讓AX寄存器與1相加:add ax,1
3.最后將值賦值給內(nèi)存空間:mov 地址2,ax
段寄存器
我們這里再重點(diǎn)介紹下段寄存器,前面我們說了相關(guān)部件提供段地址和偏移地址給地址加法器進(jìn)行合成地址,到底是什么提供的的呢,就是段寄存器,包括:代碼段寄存器(CS)、數(shù)據(jù)段寄存器(DS)、堆棧寄存器(SS)、附加段寄存器(ES)。
- CS和IP
CS為代碼段寄存器,IP為指令寄存器,它們指示了CPU當(dāng)前要讀取指令的地址,任意時(shí)刻8086都會(huì)將CS:IP指向的指令作為下一條要需要取出執(zhí)行的指令,也就是說將要被執(zhí)行的指令都是從CS:IP取出地址合成的。指令執(zhí)行流程總結(jié)如下:
1.從CS:IP指向的內(nèi)存單元讀取指令,讀取的指令進(jìn)入指令緩沖器
2.IP=IP+所讀取指令的長(zhǎng)度,從而指向下一條指令
3.執(zhí)行步驟1,重復(fù)這個(gè)過程
CPU從何處執(zhí)行指令是由CS、IP中的內(nèi)容決定的,我們可以通過改變CS、IP的內(nèi)容來控制CPU執(zhí)行目標(biāo)指令,MOV能修改大部分寄存器中的值,但是不能用于修改CS:IP的值,JMP等轉(zhuǎn)移指令能修改CS:IP中的值。比如:
jmp 2AE3:3,執(zhí)行后:CS=2AE3H,IP=0003H,CPU將從2AE33H處讀取指令。
jmp 3:0B16,執(zhí)行后:CS=003H,IP=0B16H,CPU將從00B46H處讀取指令。
綜合上述,“jmp 段地址:偏移地址”指令的功能為:用指令中給出的段地址修改CS,偏移地址修改IP。jmp 也可以直接jmp寄存器來修改IP的值,比如:jmp ax,就是設(shè)置IP的值為寄存器里面的值。 - DS和[address]
CPU要讀寫一個(gè)內(nèi)存單元時(shí),必須要先給出這個(gè)內(nèi)存單元的地址,在8086中,內(nèi)存地址由段地址和偏移地址組成,8086中有一個(gè)DS段寄存器,通常用來存放要訪問數(shù)據(jù)的段地址,比如:
mov bx,1000h //將地址放到bx中
mov ds,bx //將bx中的值賦值到ds數(shù)據(jù)段寄存器,段地址就是1000h
mov al,[0] //將10000H(1000:0)中的內(nèi)存數(shù)據(jù)賦值到al寄存器中
mov al,[address]的意思將DS:address中的內(nèi)存數(shù)據(jù)賦值到al寄存器中,由于al是8位寄存器,所以是將一個(gè)字節(jié)的數(shù)據(jù)賦值給al寄存器,8086不支持將數(shù)據(jù)直接送入段寄存器中,mov ds,1000H是錯(cuò)誤的。這里在補(bǔ)充一個(gè)知識(shí)點(diǎn)大小端,大段模式:數(shù)據(jù)的高字節(jié)存保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高字節(jié)中,小段模式剛好相反。
基礎(chǔ)指令
mov指令
mov 寄存器, 數(shù)據(jù) 比如:mov ax,8 ;將十進(jìn)制8傳送到ax
mov 寄存器, 寄存器 比如: mov ax,bx ;將bx寄存器中的數(shù)據(jù)傳送到ax
mov 寄存器,內(nèi)存單元 比如:mov ax,[2000h] ;將地址2000H單元的內(nèi)容傳送到AL寄存器
mov 內(nèi)存單元,寄存器 比如:mov[2000h],ax ;將ax傳送到地址2000h中
mov 段寄存器,寄存器 比如:mov ds,ax ;將ax中的數(shù)據(jù)傳入到ds中
mov 寄存器,段寄存器 比如 mov ax ,ds ;將ds中的數(shù)據(jù)傳入到ax中
注意事項(xiàng)如下:
- 兩個(gè)存儲(chǔ)單元之間不能直接傳送數(shù)據(jù),即:MOV指令只允許一個(gè)操作數(shù)在存儲(chǔ)器中。MOV [SI],[2000H];這是錯(cuò)誤的
- MOV指令中立即數(shù)不能直接傳送給段寄存器(CS、DS、SS、ES)和IP;段寄存器之間不能直接傳送。MOV IP,2000H ;這是錯(cuò)誤的
- CS和IP不能作為目的操作數(shù)。MOV CS,AX ;這是錯(cuò)誤的
- MOV指令中立即數(shù)不能作目標(biāo)操作數(shù)。MOV 2000H,[SI] ;這是錯(cuò)誤的
- MOV指令中的源操作數(shù)絕對(duì)不能是立即數(shù)和代碼段CS寄存器
- MOV指令中絕對(duì)不允許在兩個(gè)存儲(chǔ)單元之間直接傳送數(shù)據(jù)
- MOV指令中絕對(duì)不允許在兩個(gè)段寄存器之間直接傳送數(shù)據(jù)
- MOV指令不會(huì)影響標(biāo)志位
add指令和sub指令都跟mov一樣,都有兩個(gè)操作對(duì)象,注意事項(xiàng)也是一樣,這里就不列舉,詳細(xì)了解請(qǐng)自行查看官方文檔。
棧
函數(shù)、方法,我們平時(shí)開發(fā)是使用非常頻繁,我們看看函數(shù)最簡(jiǎn)單的的結(jié)構(gòu):函數(shù)名,函數(shù)參數(shù),返回值,平時(shí)也在說函數(shù)調(diào)用棧,其實(shí)函數(shù)調(diào)用其底層就是對(duì)棧的操作,我們想徹底搞懂函數(shù)調(diào)用,我們必須先來認(rèn)識(shí)棧,棧:是一種具有特殊的訪問方式的存儲(chǔ)空間(后進(jìn)先出,Last In Out First,LIFO)。
- 8086會(huì)將CS作為代碼段的段地址,將CS:IP指向的指令作為下一條需要取出執(zhí)行的指令
- 8086會(huì)將DS作為數(shù)據(jù)段的段地址,mov ax,[address]就是取出DS:address的內(nèi)存數(shù)據(jù)放到ax寄存器中
- 8086會(huì)將SS作為棧段的段地址,任意時(shí)刻,SS:SP指向棧頂元素
- 8086提供了PUSH(入棧)和POP (出棧)指令來操作棧段的數(shù)據(jù)。
比如push ax是將ax的數(shù)據(jù)入棧,如下所示:
總結(jié)下,本篇文章介紹了編程語言的發(fā)展,以及匯編與高級(jí)語言的關(guān)系,通過對(duì)CPU、內(nèi)存、寄存器、指令以及棧的學(xué)習(xí),希望你能抓住關(guān)鍵點(diǎn)快速入門,下一篇將正式搭建環(huán)境編寫匯編代碼。敬請(qǐng)期待,祝學(xué)習(xí)快樂!