1基礎知識
機器語言是機器指令的集合,由0和1組成,但是很長很復雜,匯編語言因此產生,
匯編語言的主體是匯編指令,匯編指令是機器指令的便于記憶的書寫格式,
程式員寫完匯編指令通過編譯器轉換為機器碼,機器碼再傳到計算機執行,
匯編語言有以下三類:
1匯編指令:助記符,有對應機器碼
2.偽指令:沒有對應機器碼,編譯器執行計算機不執行
3.其他符號:+ -等由編譯器識別,沒有對應機器碼
匯編語言的核心是匯編指令,決定了匯編語言的特性
CPU是計算機的核心部件,他控制整個計算機的運作并運算,指令和資料在存盤器中存放,也就是記憶體,CPU離不開記憶體,記憶體中指令和資料沒區別,都是二進制,CPU來識別是資訊還是指令,
一個存盤單元存盤1Byte
CPU從記憶體中讀寫書,要指定地址,指定進行哪種操作,CPU通過總線連接其他芯片,傳輸資訊
存盤單元的地址(地址資訊)->地址總線
器件的選擇,讀或寫的命令(控制資訊)->控制總線
讀或寫的資料(資料資訊)->資料總線
]()
地址總線
一根導線有兩種穩定狀態代表0和1,那么10根導線就有2^10次方個不同資料,從0到1023
地址總線的寬度決定了CPU的尋址能力
資料總線
8根資料總線可傳送一個8位二進制資料 8bits = 1byte
資料總線的寬度決定了CPU與其他器件進行資料傳送時的一次資料量
控制總線
控制總線的寬度決定CPU對外部器件的控制能力
主板上都是核心器件,CPU、存盤器等,CPU通過總線向介面卡發送指令,介面卡控制外設進行作業
隨機存盤器RAM 只讀存盤器ROM
BIOS(Basic input/putput system)

CPU將各類存盤器看作一個邏輯存盤器,所有的物理存盤器被看作一個由若干個存盤單元組成的邏輯存盤器,每個物理存盤器在這個邏輯存盤器中占有一個地址段,即一段地址空間,
記憶體地址空間的大小受CPU地址總線寬度的限制,不同計算機系統記憶體地址分配情況不同
2.暫存器
CPU由運算器、控制器、暫存器等器件構成,器件靠內部總線連,與之前總線(外)不同
暫存器程式員可以用指令讀寫
8086CPU的14個暫存器
AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
通用暫存器:
用來存放一般性資料 AX BX CX DX
16位暫存器可以拆成兩個8位暫存器使用 AH AL,高8位和低8位

兩位元組byte構成一個字word,一個位元組8bit放在8位暫存器
匯編指令
mov ax,18
mov ah,78
add ax,8
mov ax,bx
add ax,bx
...
16位暫存器只能存放4位十六進制資料,1044CH最高位的1就不能保存再ax中004CH
獨立使用AL這個暫存器如果進位是不會儲存在AH中
資料傳送和運算時 指令的兩個操作物件的位數應當一致
物理地址
所有記憶體單元構成的存盤空間是一個一維的線性空間,每一個記憶體單元在這個空間中都有唯一的地址
CPU向地址總線發出物理地址前,要在內部形成這個物理地址
16位的CPU能夠一次性處理、傳輸、暫時存盤16位地址
8086CPU有20位地址總線,內部用兩個16位地址合成一個20位 的物理地址

物理地址 = 段地址 x 16(基礎地址) + 偏移地址
段地址 x 16 常用說法左移4位(二進制位)相當于16進制左移1位
X 進制左移1位相當于乘 X

因為內部結構是這樣,為了達到20位尋址能力,利用兩個16位地址可以達到目的

接著段地址,記憶體沒有分段,段的劃分來自cpu
段地址 x 16是16的倍數,所以一個段的起始地址也是16的倍數;偏移地址位16位,16位地址的尋址能力位64kb,所以一個段的長度最大為64kb
CPU可以用不同的段地址和偏移地址形成同一個物理地址
8086的4個段暫存器CS、DS、SS、ES,當訪問記憶體由這四個段暫存器提供記憶體單元段地址
CS為代碼段暫存器 IP為指令指標暫存器
任意時刻,設CS內容為M,IP中內容為N,8086CPU從記憶體M x 16 + N單元開始,讀取指令并執行
讀取一條指令后,IP中的值自動增加(指令長度),以使CPU可以讀取下一條指令
CPU將CS:IP指向的記憶體地址單元內容看作指令
同時修改CS、IP的內容可以用 jmp 段地址 :偏移地址
僅修改IP可用 jmp 某一合法暫存器(用暫存器中的值修改IP)?
8086機編程時,可以根據需要,將一組記憶體單元定義為一個段,
將長度為N<=64的代碼,存在一組地址連續、起始地址為16的倍數的記憶體單元作代碼段
用CS:IP指向的內容就能讓代碼段的內容執行
3.暫存器(記憶體訪問)
在記憶體中存盤時,記憶體單元是一個位元組byte單元,則一個字Word要用兩個地址連續的記憶體單元來存放,低位位元組存放在低地址單元,高位位元組存放在高地址單元
字單元:由兩個地址連續的記憶體單元組成,起始地址為N的字單元簡稱為N地址字單元

0地址字單元4E21H,1地址字單元124EH......
DS和[address]
8086中有一個DS暫存器,通常用來存放要訪問資料的段地址
mov bx,1000H
mov ds,bx
mov al,[0]
將10000H(1000:0)中的資料讀到al中
這里[...]表示一個記憶體單元,括號里面表示偏移地址,指令執行時8086CPU自動讀取ds中資料作為記憶體單元的段地址
8086CPU不支持將資料直接送入段暫存器,只好用一個暫存器中轉
16位結構,有16根數線,所以一次可以傳送16位資料,也就是一個字
mov add sub都是帶有兩個操作物件的指令,而jump具有一個
編程時根據需要定義資料段,可以在具體操作的時候用ds存放資料段的段地址,再根據需要,用相關指令訪問資料段中的具體單元
堆疊
堆疊是一種具有特殊訪問方式(最后進入空間的資料,最先出去)的存盤空間


堆疊的兩個基本操作:入堆疊和出堆疊,入堆疊就是將新的元素放到堆疊頂,出堆疊就是將堆疊頂元素取出一個
堆疊的操作規則被稱為:LIFO(Last In First Out,后的進先出來)
編程時,可以將一段記憶體當作堆疊
push ax 將ax中資料入堆疊
pop ax 從堆疊頂取出資料到ax
操作都是以字為單位進行的
8086CPU中任意時刻,段暫存器SS(堆疊的段地址):暫存器SP(偏移地址)指向堆疊頂元素,push指令和pop指令執行時,CPU從SS和SP中得到堆疊頂地址
push ax兩步走
1> SP=SP-2,SS:SP指向當前堆疊頂前面的單元,作為新堆疊頂
2> 將ax中的內容送入SS:SP指向的記憶體單元處,SS:SP此時指向新堆疊頂
入堆疊時,堆疊頂從高地址向低地址方向增長,
pop ax兩步走
1> 將SS:SP指向的記憶體單元處資料送入ax
2> SP=SP+2,SS:SP指向當前堆疊頂下面的單元,作為新的堆疊頂
值得注意的時,出堆疊后pop操作前的堆疊頂元素仍然存在,但是它已經不在堆疊中,再次push后會在那里寫入新資料覆寫
由上面資料不在堆疊中進而可以思考堆疊頂超界的問題
8086CPU不保證我們對堆疊的操作不會超界
當我們把一段記憶體當作堆疊空間,當堆疊滿時再執行push堆疊頂超出堆疊空間,堆疊空間外資料被覆當堆疊空時再次執行pop堆疊頂超出了堆疊空間,而超出的地方的資料會被覆寫,自己需要注意
用堆疊暫存以后需要恢復暫存器中的內容時,出堆疊順序和入堆疊順序相反
push和pop實質上是一種記憶體傳送指令
編程時根據需要可定義堆疊段
段的綜述
對于資料段,將它的段地址放在DS中,用mov、add、sub等訪問記憶體單元的指令時,CPU就將我們定義的資料段中內容當作資料來訪問
對于代碼段,將它的段地址放在CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執行我們定義的代碼段中的指令
對于堆疊段,將它的地址放在SS中,將堆疊頂單元的偏移地址放在SP中,這樣CPU進行堆疊操作時,將我們定義的堆疊段當作堆疊空間用
由此可見CPU將記憶體中內容當作什么,是因為相應的段暫存器指向了那里
4.第一個程式
一個源程式從寫出到執行的程序
第一步:撰寫匯編源程式
第二步:對源程式進行編譯鏈接
使用匯編語言編譯程式對源程式檔案中的源程式進行編譯,產生目標檔案;再用鏈接程式對目標檔案進行鏈接,生成可在作業系統中直接運行的可執行檔案,
可執行檔案包含兩部分內容
1)程式(從源程式中的匯編指令翻譯過來的機器碼)和資料(源程式定義的資料)
2)相關的的描述資訊(比如,程式多大、占多少記憶體空間等)
第三步:執行可執行檔案中的程式
作業系統依照可執行檔案的描述資訊,將可執行檔案中的機器碼和資料載入記憶體,開始相關初始化,然后由CPU執行
源程式
匯編語言寫的源程式,包括偽指令和匯編指令,其中偽指令由編譯器來處理,程式是指源程式中由計算機執行、處理的指令或資料
偽指令:
1>segment 和 ends成對使用,功能是定義一個段,segment 說明一個段開始,ends 說明一個段結束
格式: 段名 segment
.
段名 ends
2>end是一個匯編程式的結束標志,編譯器碰到它就結束編譯,注意區分ends
3>assume 含義為假設,它假設某一段暫存器和程式中的某一個用segment...ends定義的段相關聯,
比如code segment ... code ends就定義了一個名為code的段
在程式開頭,用assume cs:code 將用作代碼段的段code和CPU中的段暫存器cs 聯系起來
DOS(一個單任務作業系統)
一個程式p2在可執行檔案中,則必須有一個正在運行的程式p1,將p2從可執行檔案中加載入記憶體,將CPU的控制權交給p2,p2才能運行,p2開始運行后,p1暫停運行
而當p2運行完,CPU控制權應交還給p1
這個程序叫做:程式回傳
任何通用的作業系統,都要提供一個稱為shell(外殼)的程式,用戶使用這個程式來操作計算機系統進行作業
DOS啟動時,先完成其他重要初始化作業,然后運行command.com,command.com運行后,執行完其他的相關任務后,在螢屏上顯示出由當前盤符和當前路徑組成的提示符
比如C:\
用戶輸入的指令 cd dir 等由command執行
執行一個程式,command 首先根據檔案名找到可執行檔案,然后將可執行檔案加載入記憶體,設定CS:IP指向程式的入口,此后,command 暫停運行,CPU運行程式,程式運行結束后,回傳到command中,command 再次顯示由當前盤符和當前路徑組成的提示符,等待用戶輸入
在DEBUG中,command將debug加載入記憶體,而debug將程式加載入記憶體,所以程式結束后回傳到debug中,Q可以回傳到command
mov ax,4c00h
int 21h
這兩條指令所實作的就是程式回傳,在程式末尾使用
edit
編輯程式
masm
匯編編譯器,接收默認檔案擴展名為 .asm,如果不是就要將檔案擴展名寫出
輸入源程式檔案名要指明路徑,除非它就在當前路徑下
masm 1t.asm / masm 1t
簡化程序,最后加上 ; 忽略中間檔案的生成
link
聯結器,接收默認檔案擴展名 .obj,如果不是就要將檔案擴展名寫出
對編譯生成的目標檔案進行鏈接,從而得到可執行程式, 輸入目標檔案名要指明路徑,除非它就在當前路徑下
link 1t.obj / link 1t
簡化程序,最后加 ; 忽略中間檔案的生成
學習匯編主要目的,通過用匯編語言進行編程而深入地理解計算機底層的基本作業機理,達到可以隨心所欲控制計算機的目的,匯編語言編程用到的工具在作業系統是運行,暫時不做過多探究

Debug
資料在Debug中默認所有資料用十六進制表示
遇到int 21h時要用P命令執行
載入.EXE
DOS系統中.EXE檔案中程式加載,cx 中存放了程式的長度
.exe裝入記憶體后,程式被裝入記憶體的什么地方?

5.[BX]和loop指令
1.bx
用[0]表示一個記憶體單元時,0表示單元的偏移地址,段地址默認在ds中,單元的長度(型別)可以由具體指令中的其他操作物件(比如暫存器)中指出
[bx]同樣也表示一個記憶體單元,它的偏移地址在bx中
inc bx的含義是bx中的內容加1,執行后bx = 2
2.loop
正如它的意思回圈
loop指令的格式是: loop 標號
CPU執行 loop指令時兩步走(這里圓括號代表一個暫存器或記憶體單元的內容)
1>(cx)=(cx)-1
2>判斷cx中的值,不為零則轉至標號處執行程式,如果為零則向下執行
通常我們用loop指令來實作回圈功能,cx中存放回圈次數
程式框架如下
mov cx,回圈次數
s:
回圈執行的程式段
loop s
除錯/執行程式時
大于9FFFh的十六進制資料A000H、A001H...FFFFH在書寫時以字母開頭,但在匯編源程式中,資料不能以字母開頭,所以前面要加0,比如0A001H
在程式執行時 loop s 中的標號 s 已經變為了一個地址
我們只想跟蹤回圈的程序時,可以用DUBUG里命令G來達到目的,一次執行完標號前的內容,g 0012表示執行程式到當前代碼段(段地址在cs中)的0012h處
當進入回圈后,我們想要回圈一次執行完,可以用p命令來達到目的,DEBUG就會自動重復執行回圈中指令,直到(cx)=0為止
Debug和匯編編譯器masm對指令不同處理
在Debug中,mov ax,[0] 表示將ds:0處的資料送入ax中
但在匯編源程式中,這個指令被編譯器當作指令mov ax,0處理
Dubug將它解釋為idata是一個記憶體單元
編譯器將[idata]解釋為 idata
目前的方法是將偏移地址送入bx暫存器中,用[bx]的方式來訪問記憶體單元
但是這樣比較麻煩,還有一種方法是在[ ]的前面顯式地給出段地址所在的段暫存器
段前綴
用于顯式地指明記憶體單元的段地址的ds: cs: ss: es:,在匯編語言中成為段前綴
比如訪問2000:0單元
mov ax,2000h
mov ds,ax
mov al,ds:[0]
loop和[bx]的聯合應用
在實際編程中,經常會遇到用同一種方法處理地址連續的記憶體單元中的資料問題,我們需要每次回圈的時候,按照同一種方法來改變要訪問的記憶體單元的地址
mov al,[bx] 中bx就可以看作一個代表記憶體單元地址的變數,我們可以通過改變 bx 中的數值,改變訪問的記憶體單元
計算ffff:0~ffff:b單元中的資料和,結果存盤在ds中
1.運算和的結果是位元組型資料,范圍在0-255之間,12個結果相加不會大于65535,dx能放下
2.不能將ffff:0~ffff:b中的資料累加到ds中,在這里面資料是8位的,不能直接加到16位暫存器dx中,也不能累加到dl中,dl會進位丟失
解決方案
用一個16位暫存器做中介,將記憶體單元中的8位資料賦值到一個16位暫存器ax中,再將ax中的資料加到dx上,從而使兩個運算物件的型別匹配并且結果不會超界
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0
mov dx,0
mov cs,12
s:
mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
一段安全的空間
8086模式中,隨意向一段空間寫入內容很危險,這段空間可能存放重要的系統資料或代碼
dos和其他合法程式一般都不會使用0:200~0:2ff的256位元組空間,使用這段空間是安全的,謹慎起見我們還可以debug查看
6.包含多個段的程式
在代碼段中使用資料
之前提到的那一段安全的空間只有256位元組,我們需要超過256個位元組的空間該怎么辦?在作業系統的環境中,合法地通過作業系統取得地空間都是安全的,因為作業系統不會讓一個程式所用的空間和其他程式以及系統自己的空間相沖突,
程式取得所需空間的兩種方法:
1.在加載程式的時候為程式分配
2.在程式執行的程序中向系統申請
我們若要一個程式在被加載的時候取得所需的空間,則必須要在源程式中做出說明
當可執行檔案中的程式被加載入記憶體時,這些定義的資料同時也被加載入記憶體,與此同時,我們要處理的資料自然而然地獲得了存盤空間
dw 0123h,"dw"的含義是定義字形資料即define word
編程計算0123h、0456h、0789h、0abch、0defh、0cbah、0987h的和,結果存在ax中
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
start: mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
start這個標號在end后出現,偽指令end除了通知編譯器程式結束外,還可以通知編譯器程式的入口在什么地方,end指令指明了程式的入口在標號start處
偽指令end描述了程式的結束和程式的入口,在編譯、鏈接后,由"end start"指明的程式入口,被轉化為一個入口地址,存盤在可執行檔案的描述資訊中
利用這種方法可以安排程式框架
assume cs:code
code segment
資料
start:
代碼
code ends
end start
在代碼段中使用堆疊
我們需要堆疊空間,當然也要由系統分配,正如上面定義資料,資料就能載入記憶體,所以可以在程式中通過定義資料來取得一段空間,然后將這段空間作為堆疊空間使用
dw 0,0,0,0,0,0,0,0
之后合理設定堆疊頂ss:sp,這段空間就可以當作堆疊空間
所以描述dw的作用時,可以說用它定義資料,也可以說用它開辟記憶體空間
將資料、代碼、堆疊放入不同空間
上述內容將他們放在一起程式顯得混亂,用到堆疊空間也小,代碼不長,放在一個段沒問題(8086模式一個段的容量不能大于64kb)
所以考慮用多個段存放資料、代碼
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start
定義多個段的方法和定義一個段方法一樣
對段地址的參考:在程式中段名就相當于一個標號,它代表了段地址
我們定義了三個段,作用如同的名字含義,但是計算機不知道
我們只需設定start的位置在code段(cs:ip),ss:sp指向堆疊頂,ds指向data段,其他暫存器如bx存放data段中資料的偏移地址,即可按照我們的含義分段了
7.更靈活的定位記憶體地址的方法
and指令:邏輯與指令,按位進行與運算(對應位全1為1,不然為0)
例如指令
mov al,01100011B
and al, 00111011B
執行后al = 00100011B
通過該指令可將操作物件的相應位設位0,其他位不變
or指令:邏輯或指令,按位進行或運算(對應位有1為1,全0為0)
例如指令
mov al,01100011B
or al, 00111011B
執行后al = 01111011B
通過該指令可將操作物件的相應位設為1,其他位不變,
我們要把人能看懂的資訊存盤在計算機中,就要對其及進行編碼,將其轉化為二進制資訊進行存盤,計算機要將存盤的資訊顯示出來看就需要對其進行解碼,
一個文本編輯程序中,就包含著按照ASCII編碼規則進行編碼和解碼:
鍵入"a"計算機用ASCII碼的規則對其進行編碼,將轉化為61H存盤在記憶體指定的空間中;文本編輯軟體從記憶體中取出61H,將其送到顯卡的顯存中;作業在文本模式下的顯卡,用ASCII碼的規則解釋顯存中的資料,61H當作字符"a",顯卡驅動顯示幕,將字符"a"影像畫在螢屏上
匯編程式中'......'的方式指明資料是以字符的形式給出的,編譯器將其轉換對應ASCII碼
db 'unIX'相當于db 75H,6EH,49H,58H
大小寫轉換問題
首先分析:每個小寫字母的ASCII碼值比大寫字母ASCII碼值大20H,通過此方法可以將小寫字母轉化為大寫,可這要先判斷字母本身是否是大小寫,但目前水平沒有達到
再分析:字母ASCII碼的二進制,除第五位(從0開始計數)外,大寫字母和小寫字母其他各位都一樣,大寫字母第五位為0,小寫字母第五位為1
因此可以用and 和 or指令將第五位置0或置1來改變大小寫
[bx+idata]
[bx]可以指明一個記憶體單元,[bx+idata]更靈活的指明記憶體單元,偏移地址(bx)+idata
段地址在ds中
db 'BaSiC'
我們要把這個字串全部轉為大寫,可以把這個字串看作一個陣列,首地址就是B
SI和DI
si和di是8086CPU和bx功能相近的暫存器,si和di不能分成兩個8位暫存器來使用,
[bx+si]和[bx+di]
都表示一個記憶體單元,偏移地址是(bx)+(si),段地址在ds中
[bx+si+idata]和[bx+di+idata]
都表示一個記憶體單元,偏移地址位(bx)+(si)+idata,段地址在ds中
以上就是CPU提供的多種尋址方式
當我們需要匯編中的回圈嵌套時,可以將外層回圈的cx數值保存起來,在執行外層回圈loop指令前,再恢復外層回圈cx數值,
一般來說,在需要暫存資料的時候,我們應該用堆疊
尋址方式的適當使用,是我們可以以更合理的結構來看待所要處理的資料,而為所要處理的看似雜亂的資料設計一種清晰的資料結構是程式設計的一個關鍵問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543614.html
標籤:其他
上一篇:Java執行緒中斷
