匯編語言的一些注意點
- 匯編語言是直接在硬件之上工作的編程語言。
- CPU(Central Processing Unit) 中央處理單元。
- 匯編語言和機器語言的關系
匯編語言是機器指令便于記憶的書寫形式。 - 匯編語言的三類指令
- 匯編指令:有對應的機器碼
- 偽指令:沒有對應的機器碼,由編譯器識別執行。
- 其他符號,沒有對應的機器碼,如 + - * / ,由編譯器識別執行。
- 磁盤和內存的區別
磁盤上的數據或者程序如果不讀到內存中,就無法被 CPU 使用。 - 指令和數據的區別
指令和數據是應用上的概念,在內存或者磁盤中,指令和數據沒有任何區別,都是二進制信息,關鍵看如何去理解,理解成數據,或者是指令。
在存儲器中,數據和程序以二進制的形式存放。
詳細: P4
- 存儲單元、bit、byte 的關系
一個存儲單元可以存儲 1 byte 的數據。
1 byte= 8 bit
1 byte 可以表示0~255之間的數
1 word 可以表示0~65535之間的數 - 完成對數據的讀寫,需要?
- 地址信息
- 控制信息(讀信號輸出、寫信號輸出...)
- 數據信息
- 總線
在計算機中專門有連接CPU和其他芯片的導線,叫做總線。
從邏輯上分成三類,地址總線、控制總線和數據總線。(外部總線) - CPU總線的寬度
一個CPU有N根地址線,則可以說這個 CPU 的地址總線的寬度為 N。 - 數據傳輸時,先低八位,后高八位。(想想為什么不是反過來?)
匯編指令同機器指令一一對應
- 尋址能力和地址總線寬度的關系
假設地址總線有n位,即共有n位二進制位來表示地址,那么最多可以表示2n個地址,另外,由于計算機以一個字節為尋址單位,所以CPU的尋址能力或者說最大尋址范圍為2n個字節。
參考:
地址總線、數據總線、尋址能力、字長及cpu位數等概念之間的關系
https://www.cnblogs.com/chanchan/p/7648490.html
8根數據總線一次性可以傳輸 1B 的數據。
內存地址空間
可以尋址到的內存單元構成了這個 CPU 的內存地址空間。
可以將系統的各類存儲器看作一個邏輯存儲器,通過向不同的地址操作,就是對不同的存儲器進行操作。一些概念
主板(BIOS)
接口卡:
顯示卡((ROM)BIOS、RAM),我們將需要顯示的內容寫入顯存,就會顯示在顯示器上。
網卡(BIOS)存儲器芯片的分類
從讀寫屬性上分成兩類:隨機存儲器(RAM)和只讀存儲器(ROM)
隨機存儲器可讀可寫,但是必須帶電存儲,關機后存儲器的內容丟失。
只讀存儲器只能讀取不能寫入,關機后其中的內容不丟失。如果發出向ROM的內存單元中寫入數據的操作是無效的。一個典型的 CPU 由 運算器、控制器、寄存器等器件構成,由內部總線連接。
運算器:信息處理
控制器:控制器件工作
寄存器:進行信息存儲
內部總線:連接各個器件,在它們之間進行數據的傳送。內部總線和外部總線之間的關系
內部總線實現 CPU 內部各個器件之間的聯系,外部總線實現 CPU 和 主板上其他器件的聯系。程序員通過改變各種寄存器的內容來實現對 CPU 的控制。
8086寄存器的特點
- 16個寄存器
- 都是16位的
- 通用寄存器:AX,BX,CX,DX。
段寄存器: CS,DS,SS,ES。
CX中存放的是程序的長度,單位是字節。
為了保證兼容,可以拆分成兩個8位寄存器。
AH,AL
BH,BL
...
這些8位寄存器都可以獨立使用,也就是說,不會相互影響。 - 16位寄存器能表示最大的數是 65535 。
- 出于對兼容性的考慮,8086 CPU 可以一次性處理以下兩種尺寸的數據--字(Word)和字節(Byte),字可以分成高位字節和低位字節,也可以說是高八位寄存器和低八位寄存器。
- 數制的后綴
在十六進制數后加H,在二進制數后加B,十進制數后什么也不加。 - 注意
在寫一條匯編指令或者一個寄存器的名稱時,不區分大小寫。
指令的兩個操作對象的位數應該是一致的。 - 若運算結果超出了儲存結果的寄存器的存儲范圍,則會舍去高位,留下低位。
- 物理地址
所有的內存單元構成的存儲空間是一個一維的線性空間,每一個內存單元在這個空間中都有唯一的地址,叫做物理地址。不同的CPU形成物理地址的方式不同。 - 邏輯地址
邏輯地址:CPU所生成的地址。邏輯地址是內部和編程使用的、并不唯一。例如,你在進行C語言指針編程中,可以讀取指針變量本身值(&操作),實際上這個值就是邏輯地址,它是相對于你當前進程數據段的地址(偏移地址),不和絕對物理地址相干。
資料:
物理地址和邏輯地址
https://blog.csdn.net/tuxedolinux/article/details/80317419
16位機為什么叫16位機?
處理、傳輸、暫時存儲的信息的最大長度是16位的。
處理:運算器處理16位數據
傳輸:寄存器和運算器之間的通路為16位
暫時存儲:寄存器的最大寬度為16位。8086CPU有20位地址總線,可以傳送20位地址,達到1MB尋址能力。
有16位數據總線,可以一次性傳送16位的數據。8086如何合成物理地址?
在內部用兩個16位地址合成的方法來形成一個20位的物理地址。
一個叫做段地址,一個叫做偏移地址。
物理地址=段地址*16+偏移地址。(也就是基礎地址+偏移地址=物理地址)
" *16 " 可以理解成 左移 4 位探究X進制數左移1位相當于乘以 ?
為什么段的起始地址是16的倍數
因為基礎地址=段地址*16為什么一個段的長度最大是64KB?
偏移地址為16位,尋址能力為64KB。(是8086 模式的限制,并不是所有的處理器都是這樣)可以用不同的段地址和偏移地址形成同一個物理地址。
段地址(SA)、偏移地址(EA)
偏移地址的英文縮寫為什么叫做EA?
偏移地址(Offset Address = OA),又叫有效地址(Effective Address = EA)
資料:偏移地址的英文縮寫為什么叫做EA?
https://bbs.csdn.net/topics/370157156
- 數據在 21F60H 內存單元的說法
- 數據在 2000:1F60 單元
- 數據在2000H 段中的1F60單元中
CS是代碼段寄存器,IP是指令指針寄存器。
cs:ip 讀取指令的先后順序
先讀取指令到指令寄存器,后IP更新,然后執行讀取過的指令。
(先讀取,后執行)加電啟動或者復位后,cs=FFFFH,IP=0000H,所以FFFF0單元的指令是8086PC機開機后執行的第一條指令。
指令的分類
傳送指令,大部分寄存器的值,都可以用 mov 指令來改變。
轉移指令,能夠改變 cs、ip的內容的指令被統稱為轉移指令。jmp的用法
jmp 段地址:偏移地址 可以同時改變cs和ip的值。
jmp 合法寄存器 可以改變ip的值。(16位寄存器)地址的讀取
對于一個內存片段,以最低的地址來表示該片段。
在表示內存片段的值時,高地址數據---低地址數據。
在畫圖時,低地址在上,高地址在下。段地址在段寄存器中存放,當8086CPU要訪問內存的時候,由段寄存器提供內存單元的段地址。
debug指令
R--查看寄存器的內容
R 寄存器--修改寄存器的值
d --查看預設地址處內存的值
d 段地址:偏移地址 --查看指定開始位置的內存單元的值
d 段地址:偏移地址 末尾地址 --查看區間內內存單元的值
e 段地址:偏移地址 數據 數據... --修改內存單元的值
e 段地址:偏移地址 回車--用提問的方式改值
t ---執行一條指令
t3 ---執行3條指令
t=0103 ---執行0103處的指令
a
a 段地址:偏移地址值得注意的是
b80100 是 mov ax,0001 的機器碼,在內存中是從低地址到高地址存儲的,和數據正好相反。兩位是一個單元(8位)
字單元
即存放一個字型數據(16位)的內存單元,由兩個地址連續的內存單元組成。(高位字節單元和低位字節單元)
N地址字單元,N是字單元的起始地址。內存單元
內存單元是字節單元(一個單元存放一個字節),也可以是字單元。mov的一些作用
將寄存器值、數據、內存單元的值送入寄存器中。
將寄存器的值傳入內存單元,寄存器、段寄存器。(三對三)
將段寄存器的值傳入寄存器、內存單元(
將內存單元的值傳入段寄存器、寄存器[基本上是雙向的](除去數據不能與內存單元和段寄存器直接交互)
add/sub
三寄存器一內存因為8086CPU硬件設計的問題,不能將數據直接送入段寄存器,需要使用另外一個寄存器作為中轉,從而設定段寄存器的值。
在使用ds寄存器的時候,一般都需要先設定其值
[0]和ds:[0]的使用范圍???
mov ax,[0]
在 debug 運行正常,但是在編譯的時候被翻譯成mov ax,0
,如果想要原有的作用,可以使用
mov bx,0
mov ax,[bx];默認段寄存器在ds中,當然也可以顯式的給出。
間接的尋址。
為了避免麻煩,也可以使用 mov ax,ds:[0]
的形式尋址。
- 棧,是一種具有特殊的訪問方式的存儲空間,它的特殊性在于,最后進入這個空間的數據,最先出去。LIFO(last in first out)
- 向上增長型
在入棧的時候,棧頂從高地址向低地址的方向增長。
在任意的時刻,SS:SP指向棧頂元素。
當入棧的時候,sp=sp-2,后將數據移入棧頂空間。
在出棧的時候,先將棧頂空間數據移入寄存器,后sp=sp+2 - 棧中第一個內存單元地址、棧段的最高地址、棧為空時SS:SP的指向之間的關系
SS:Sp-1=棧段的最高地址-1=棧中第一個內存單元的地址。
要自己來警惕棧頂超界的問題。
8086CPU工作不會檢查棧是否超界,它只考慮當前的棧頂在何處,從而節省資源。 - push/pop 指令的形式
push/pop 寄存器、段寄存器、內存單元
需要注意的是:棧操作都是以字為單位的。
本質上是內存傳送指令。 - 將AX寄存器清零的兩種方式
mov ax,0(三個字節的機器碼)
sub ax,ax(2個字節的機器碼) - 一段內存空間,可以是代碼的存儲空間,數據的存儲空間,也可以是棧空間,也可以什么都不是。
- 一個源程序從寫出到執行的過程
源程序的文本文件(.asm)->編譯(.obj)連接(.exe)->可執行文件(程序和數據,相關的描述信息)->加載(command)->內存中的程序->運行(CPU) - 偽指令
code segment
...
code ends(可以理解為end segment)
end(標志著程序的結束)
偽指令一般由編譯器執行,從而控制編譯器的編譯工作。
- 標號
一個標號指代了一個地址,被解析成一串數值。
所以不允許mov ds,data
存在。 - 程序返回
在單任務操作系統上(DOS)基礎上,程序返回的過程如下。
一個程序P2在可執行文件中,則必須有一個正在運行的程序P1,將P2從可執行文件中加載入內存,將CPU的控制權交給P2,P2才能運行,P2開始運行之后,P1暫時不運行。
當P2運行完畢后,應該講CPU的控制權交還給使它得以運行的程序P1,此后,P1繼續運行。(這個過程叫做程序返回)
mov ax,4c00h
int 21h
用這兩句實現程序返回。一般用于程序的末尾處。
- 語法錯誤和邏輯錯誤
- 在編譯的時候,有兩種情況得不到目標文件(.obj)
- 未能找到源程序文件
- 程序中有" severe errors"
- link 的作用
- 連接多個目標文件
- 連接某個庫中的子程序
- 連接單獨一個文件
- DOS系統的shell(外殼),是程序command.com(命令解釋器),在DOS中,command處理各種輸入:命令或要執行的程序的文件名。
- PSP(程序段前綴),是一個數據區,用于DOS和被加載程序的通信。
PSP區:SA:0
程序區:SA+10H:0
(段地址不相同)
ds=SA
cs=SA+10
PSP區有256個字節的空間。
可以推斷出有如下對應關系:因為“ds=0B2D,所以程序從0B3DH段開始,也就是說CS是0B3DH - 在debug模式下,用 p 指令來執行 int 21h。
當int 21h
被執行后,顯示出Program terminated normally
,表明返回到debug當中.然后可以用 q 指令退出debug模式,返回到 commmand 中。 - 如何完整的描述一個內存單元?
- 內存單元的地址
- 內存單元的長度
- 描述性的符號” () ”中,可以有3種類型。
- 寄存器名
- 段寄存器名
- 內存單元的物理地址(一個20位的數據)
詳細P96
描述性符號 reg 表示寄存器
sreg 表示 段寄存器
- CPU執行loop指令的時候, 要進行兩步操作,
- (cx)=(cx)-1
- 判斷 cx 中的值,不為零則轉至標號處執行程序,如果為零則向下執行。
一般情況下,用 cx 存放循環次數,用 loop 實現循環功能。
- 減少循環次數是提高計算效率的一種方式,例如 123*236 用哪個乘數作為 cx的值?
p103
- 在匯編源程序中,數據不能以字母開頭,要在前面加0
p104
- 在遇到 loop 指令的時候,使用p命令來執行,Debug可以自動重復執行循環中的指令,直到(cx)=0為止。
也可以使用 g 偏移地址 直接運行到CS:偏移地址處。
兩者有著相同的效果。 - 一個概念
一個簡單的操作系統,大概10萬行代碼 - 段前綴
諸如ds: cs: ss: es:
之類的。 - 一段安全的空間
DOS或者其他合法的程序一般都不會使用0:200~0:2ff的256個字節的空間,所以我們使用這段空間是安全的。 - dw define word,字型數據。
- 可以用來定義數據
- 可以用來開辟空間
- end的作用
- 標志程序的結束
- end 后面的標號,指明了程序的入口。
- 偽指令 assume
無需深究,只要知道需要用它將你定義的具有一定用途的段和相關的寄存器聯系起來就可以了。 - and指令(有0則0),or指令(有1則1)
將al 的第0位設定為0的指令是: and al,11111110B
將al的第0位設定為1的指令是: or al,00000001B - 按一下鍵盤的'a',在屏幕上就會顯示'a',這是什么樣的過程?
按下鍵盤的a,送入計算機中,并對其編碼,轉化為61h放入內存中,文本編輯軟件從內存中取出61h,將其送到顯卡的顯存上,工作在文本模式下的顯卡,用ASCII碼的規則解釋顯存中的內容,61h被當成'a',驅動縣市區,,將字符'a'的圖畫顯示在屏幕上。 - 一些常識
'A' 的ASCII碼是 41H
'a' 的ASCII碼是 61H
'1'-30H=1 - 一個有趣的tip
'a'-'A'=20H=2^5
表現在二進制上就是:小寫字母的二進制第五位(從右往左,從0開始)為1,大寫字母的為0,其他位相同。
通過靈活的運用and、or指令,可以將一串大小寫混合的字符串轉變成統一的大寫或者小寫。 - [bx+idata]的一些形式
[bx+idata]
=[idata+bx]
=[bx].idata
=idata[bx]
可以將idata[bx]
類比成C語言中的a[i]
所以[bx+idata]
的形式為高級語言實現數組提供了便利機制。
- si和di是8086CPU于bx功能相近的寄存器,只是不能分成兩個8位寄存器來使用。
也可以有如下形式:
[si]
[si+idata]
也可以有與bx的組合形式:
[bx+si]
[bx][si]
拓展形式:
[bx+si+idata]
[bx+di+idata]
[bx+idata+si]
200[bx+si]
[bx].200[si]
[bx][si].200
要注意的是+idata可以對應成.idata
還有bx和si/di沒有主次關系,后者也可以在前者的前面,上文中bx統一在si/di的前面只是習慣問題。
- 一般來說,在需要暫存數據的時候,我們都應該使用棧。
- 數據處理的兩個基本問題
- 處理的數據在什么地方?
- 要處理的數據有多長?
- bx、si、di、bp中
[bp]默認的段地址是ss。
bp和bx的地位相同
si、di的地位相同。
注意下面的指令是錯誤的:
mov ax,[bx+bp]
mov ax,[si+di]
- 機器指令處理的數據在什么地方?
內存、CPU內部、端口 - 對于數據的處理分為哪幾種操作?
讀取、寫入、運算。 - 立即數
對于直接包含在機器指令中的數據(執行前在CPU的指令緩存區中),在匯編語言中稱為立即數(idata). - 尋址方式
p164
直接尋址--[idata]
寄存器間接尋址--[bx]
寄存器相對尋址--[bx+idata]
基址變址尋址--[bx+si]
相對基址變址尋址--[bx+si+idata]
- 在沒有寄存器存在的情況下,用操作符 X ptr 指明內存單元的長度,X在匯編指令中可以為 word或者byte。
- div 除數(reg 或者 內存單元)
被除數 ax,ax與dx中(dx為高16位,ax為低16位)
除數8位,被除數16位。除數16位,被除數32位。
結果 除數8位,al商,ah余數
除數16位,ax商,dx余數。 - dd dword(double word),雙字型數據。
- dup的一些用法
dw 3 dup(0)
dd 3 dup(0,1,2)
db 3 dup('abc','ABC')
轉移指令對IP的修改范圍不同,可以分成短轉移和近轉移(統稱為段內轉移),遠轉移(段間轉移)
短轉移的范圍 -128~128
近轉移 -32768~32867nop 的機器碼占一個字節。
mov也可以借助偏移地址來復制指令。(本質上都是機器碼)offset 操作符,取得標號的偏移地址。
jmp short 標號 的本質 ip=ip+8位位移
8位位移=標號處的地址-jmp指令后第一個字節的地址。(在編譯的時候算出)
short--8位位移對應。
8位位移用補碼表示。
jmp near ptr 標號 的本質 ip=ip+16位位移。
與上類似。
jmp far ptr 標號 的本質 jmp 段地址:偏移地址
jmp word ptr(段內轉移)
jmp dword ptr(段間轉移) 高地址是段地址,低地址是偏移地址 (cs:ip的指向)jcxz指令,
if(cx==0) jmp short 標號;所有有條件的跳轉、循環都是短轉移。比如jcxz,loop等
loop 標號相當于
cx--
if(cx!=0) jmp short 標號;理解只涉及位移的重要性,而不是存儲實際地址。
編譯器會對轉移位移超界進行檢測。