彙編語言程序設計
這篇文章是一年之前學微機原理時寫的,現在改一下格式重發到CSDN博客上。主要內容是彙編語言。
1 彙編語言程序及其開發
源程序經過彙編成爲目標程序,再將目標程序連接成爲exe程序,最後進行執行和調試。
2 彙編語言源程序的結構
2.1 執行性語句和說明性語句
彙編語句分爲執行性語句和說明性語句。
執行語句最後會轉變爲機器碼,即01數據存儲在內存中;
說明性語句由編譯器處理,不會生成機器碼。
執行性語句格式:
a:mov ax,0; // 將ax寄存器置0
說明性語句格式:
a dw 3; // 在內存中申請一個字的空間,在裏面放入0003,這段空間的起始地址爲a。
2.2 定義段
比如:
stacksg segment para stack 'stack'
段名 定位類型 連接方式 類別名
段名:stacksg即此段的起始地址 。
定位類型:有四種定位類型:
- para類型:表示此段的起始地址的最低四位必須是0;
- page類型:表示此段的起始地址的最低八位必須是0;
- word類型:表示此段的地址必須爲偶數;
- byte類型:表示此段可以從任何地址開始。
默認的缺省定位類型爲para類型。
連接方式:如果我們有多個目標程序,我們對他們進行連接的時候就要使用連接方式。現在我們只需要學習at連接方式,at後面加一個地址,表示這個段的段地址,at連接方式不能用於代碼段。
類別名:也是在連接時使用的。
常見的定義段的方式:datasg segment at 1000h
2.3 equ僞指令
equ是等價的意思,相當於c++中的引用。
比如:
const equ 10h
這裏我們定義了一個const,它等價於10h。
需要注意的是,用equ定義的符號名在同一程序只能定義一次,不許重複定義。
2.4 =僞指令
比如:
const = 10h
這裏的const允許在後面再進行更改,也就是說可以重複定義。
2.5 數據標號
比如:
a dw 0,0,0,0
這句話在內存中分配4個字的空間,都賦值成0。
這裏a就是數據標號,它同時描述了內存地址和單元長度。從今以後,a就是這四個零的地址,並且說到a必須是字,把它賦給字節或者雙字都不行。
一種特殊的數據標號指令:
a dw ?
這裏在內存中費配了一個字的空間。使用?的意思是隻開闢空間而不進行初始化。
2.6 dup僞指令
比如:
a db 100 dup (00h)
表示開闢一百個字節空間,並全都賦值成00h
2.7 ret和retf
ret指令用棧中的數據,修改ip的內容,從而實現近轉移。
retf指令用棧中的數據,修改cs和ip的內容,從而實現遠轉移。
CPU執行ret指令時,相當於執行pop IP
CPU執行retf指令時,相當於執行pop IP pop CS
2.8 過程定義僞指令proc和endp
過程就是子程序。一般會使用call指令調用子程序,子程序的最後一條指令一般是ret,用於返回shell。
比如:
delay proc near
push cx ; // 將cx的值入棧,以免丟失,等子程序運行完,返回原程序時再出棧
mov cx,30h ; // 通過設置cx的值改變延時的時間
wait:loop wait
pop cx ; // cx出棧
ret ; // 返回原程序
delay endp
其中的near
表示是段內轉移,如果是段間轉移的話要換成far
。
delay
和endp
必須成對出現。
2.9 org指令
用來設置偏移地址。
比如:
data segment
a db 23,56
org 2000h
data ends
表示a的偏移地址爲2000h
3 常數、變量及標號
一個字符對應一個字節。
比如:‘a’對應41h
4 表達式和運算符
4.1 算數運算符
±*/:加減乘除
mod:取餘
shl和shr:shl和shr是邏輯移位指令。
shl是邏輯左移指令,它的功能爲:
- 將一個寄存器或內存單元中的數據向左移位;
- 將最後移出的一位寫入CF中;
- 最低位用0補充。
比如:
mov al,01001000b
shl al,1 ;將al中數據左移一位
執行後(al)=10010000b,CF=0。
注意:
如果移動位數大於1時,必須將移動位數放在cl中。
比如:
mov al,01010001b
mov cl,3
shl al,cl
執行後(al)=10001000b,因爲最後移出的一位是0,所以CF=0。
shr是邏輯右移指令,它和shl所進行的操作剛好相反。
4.2 邏輯運算符
- and:與
- or:或
- xor:異或
- not:非
4.3 關係運算符
使用關係運算符進行邏輯判定,如果判定結果是真返回全1(0FFFFH),否則返回0。
- eq:等於
- ne:不等於
- lt:小於
- le:小於等於
- gt:大於
- ge:大於等於
關係運算符一般不單獨使用,常與其他運算符結合使用。
比如:
// a大於60時,給al賦值50;a小於等於60時,給al賦值70
mov al,((a gt 60) and 50) or ((a le 60) and 70);
4.4 seg和offset
seg給出段地址,offset給出偏移地址。
4.5 type
type操作符返回一個表示操作數的類型的數值。
字節類型返回1,字類型返回2,雙字類型返回4,near標號返回-1(0FFFFH),對於far標號返回-2(0FFFEH)。
4.6 length和size
一般與dup連用。
比如定義
a dw 50 dup(0000)
則:length(a)=50 size(a)=100
其中size = length*type
另外對於用非dup定義的length返回1。
4.7 high和low
high和low分別返回字的高字節和低字節。
比如:
a = 2050h
mov al,low a;// al = 50h
mov ah,high a;// ah = 20h
4.8 $運算符
$是彙編語言中的一個預定義符號,等價於當前正彙編到的段的當前偏移值。
比如最常用的JMP $就是指轉移到當前執行語句的地址,在這的效果就是原地跳轉,不再向後執行了。
指令“jmp $+3”中的“$”表示當前這條指令在代碼段中的偏移量。
指令“jmp $+3”表示要向前跳轉到距離這條指令3個字節的地方。
若是“jmp $-3”,則表示要向後跳轉到距離這條指令3個字節的地方。
$常見於動態定義數據長度,例如:
MyData db '123456789abcdefgh'
DataLength EQU $ - MyData
4.9 ptr
指明是字單元還是字節單元
比如:
mov word ptr ds:[0],12h
5 8086/8088CPU彙編程序的輸入與輸出
使用DOS的功能通常要進行如下4個步驟
- 在AH中設置功能號
- 在指定寄存器中設置入口參數
- int 21h
- 得到出口參數,可以用來分析執行的情況
5.1 輸入單個字符
功能號:01h
入口參數:用戶從鍵盤輸入
出口參數:al = 所輸入字符的ASCII碼
功能:用戶從鍵盤輸入一個字符,輸入字符後返回,同時在屏幕上顯示所輸入的字符。ctrl+c退出。
比如:
mov ah,1
int 21h
5.2 輸入字符串
功能號:0ah
入口參數:ds:dx指向字符串緩衝區的首地址,緩衝區的第一個字節存放最多接收的字符個數(包括回車)。入口參數由用戶輸入。
出口參數:ds:dx指向的第二字符存放實際輸入的字符個數(不包括回車),從第三個字符開始存放輸入的字符串。若實際輸入的字符數比最大字符數多,多出來的字符會被丟棄並響鈴。
比如:
buffer db 81 ;定義緩衝區,最多允許輸入81個字符(包括回車)
db 0 ;定義實際存放的字符數
db 81 dup (0) ;存放輸入的字符串
mov dx,seg buffer ;取得buffer的段地址
mov ds,dx
mov dx,offset buffer;取得buffer的偏移地址
mov ah,0ah
int 21h
5.3 顯示單個字符
功能號:02h
入口參數:dl=要顯示的字符的ASCII碼
功能:在顯示器當前光標出顯示單個字符,然後光標右移一個位置。ctrl+c退出。
比如:
mov ah,02h
mov dl,41h
int 21h
5.4 顯示字符串
功能號:09h
入口參數:ds:dx指向字符串的首地址,注意字符串必須以$結束。
功能:在顯示器上輸出指定的字符串。
比如:
string db 'hello$'
mov dx,seg string
mov ds,dx
mov dx,offset string
mov ah,09h
int 21h
5.5 磁盤輸入輸出
說明:
- 文件名字符串以0作爲結束標誌。
- cx中的文件屬性有:00(標準文件)、01(只讀文件)、02(隱含文件)、04(系統文件)。
- ax中的錯誤碼有:1(無效功能號)、2(文件沒找到)、3(路徑未找到或文件不存在)、4(打開文件太多)、5(拒絕存取)。
6 高級彙編技術
6.1 宏彙編
宏是爲了減輕程序猿的負擔,它有點像高級語言中的函數。
我們先會定義一個宏,然後在需要它的時候使用它。編譯器看到我們使用宏,會在這個地方把代碼補全。
定義宏:
a macro x,y,z ;;a是宏名,xyz都是形參
mov ax,0
endm ;;宏以endm結束
宏定義一般出現在程序的開頭。
宏定義中的註釋要以;;開始
調用宏:
a 4,al,al