前言
從本博文開始,我將主要學習NASM的語法格式,輔以MASM語法的相關了解
一個最簡單的helloword
; 功能描述 helloword org 07c00h ; 告訴編譯器程序加載到7c00處,否則strMess位置的計算會出錯的 section .data strMess: db "Hello, OS world!" strlen equ $-strMess ; 計算 strmess的長度
section .text mov ax, strMess mov bp, ax ; ES:BP = 串地址 mov cx, strlen ; CX = 串長度 mov ax, 01301h ; AH = 13, AL = 01h mov bx, 000ch ; 頁號為0(BH = 0) 黑底紅字(BL = 0Ch,高亮) mov dl, 0 int 10h ; 10h 號中斷 ret jmp $ ; 無限循環
流程控制
1. if-else
if-else 語句其實就是 cmp + 條件判斷語句(如, je,ja,jb jne...)語句的組合
原理: 1. 使用cmp來調整標記寄存器的的標記位
2. 使用對應的條件判斷指令來實現條件轉跳,條件轉跳指令會根據CMP指令對標志寄存器進行判斷從而根據結果進行轉跳
例子:
mov ax, 2 cmp ax, 2 je a ; if( eax = 2) jmp exit ;else a: cmp ax, 1 ja b ; if(eax > 1) jmp exit ;else b: cmp ax, 3 jb c ; if(eax < 3) jmp exit ;else c:
2. while 和 for 循環
while是使用了loop指令,loop指令就是判斷CX是否為0,如果cx=0,那么啥都不做,如果cx 不等於 0 則 cx-- 然后轉跳
例子
; while循環
mov cx, 10 mov bx, 0 add: add bx,1 loop add ; while(ecx != 0)
調用子程序
調用子程序其實不像C/C++那樣有明確的界限,你可以使用任意的過程轉跳指令去調用子程序,可以用非條件轉移指令jmp也可以使用條件轉移指令 je ja jb 等等,我現在所說的就是一般意義上用到的子程序調用指令 call和ret、retf。
Call和jmp一樣也分為段內和段間調用,對應的返回函數就分別是ret和retf,ret是段內返回,retf就是段間返回。
Call和jmp的區別在於 call指令在jmp之前將當前地址給壓棧了。
;段內調用 Call: Push ip Jmp newip Ret pop ip ;段間轉移 Call: Push cs Push ip Jmpnewcs:newip Retf: pop ip Pop cs
具體語法MASM和NASM是略有不同的
MASM:
段內: Call sub1 ; sub1是子過程 Call bx Call [bx] Call var ; var是變量 段間: Call sub1 Call far ptr sub1 Call dword ptr [bx] Call var
masm 有proc偽指令,用於聲明過程調用語句,具體語法如下
過程調用名 PROC [NEAR|FAR]
....
過程調用名 ENDP
所以MASM可以顯式的指明過程調用需要的是段內還是段間調用。
NASM
段內: Call sub1 Call 立即數 Call bx Call var 段間: Call far [bx] Call 立即數:立即數 Call dword var
我不清楚nasm是否能針對標簽識別出是段內和段間調用,但是我沒發現顯式聲明使用標簽的段間調用指令,但是我看到有博客說,nasm的16位匯編模式是沒有段這個概念。我猜意思可能是段這個概念需要編程者自己去管理,nasm認為所有代碼都在同一個段內。
參數傳遞
匯編的參數傳遞是很靈活,具體只要調用方和被調用方約定好基本是隨便傳遞,基本有三種方式
1. 寄存器傳遞參數
2.內存傳遞參數
3.堆棧傳遞參數
其中有幾點需要注意
1. 環境的保護,意思是在子過程中肯定要用到標志寄存器和一些其他寄存器,但是如果這里面有調用者重要的信息的話數據就被破壞了會導致調用者的異常。
2. 數據的清理,比如使用堆棧傳遞數據,使用完之后由調用者清理還是被調用者清理。
具體可以看一下c/c++的幾個調用約定,stdcall fastcall 等等。。
最后引用一段從網上的一個文檔中看到的話,不過seg和wrt只有在編譯格式是obj的時候才能用。
《nasm中文手冊》
3.6 `SEG'和`WRT' 當寫很大的16位程序時,必須把它分成很多段,這時,引用段內一個符號的 地址的能力是非常有必要的,NASM提供了'SEG'操作符來實現這個功能。 'SEG'操作符返回符號所在的首選段的段基址,即一個段基址,當符號的偏 移地址以它為參考時,是有效的,所以,代碼: mov ax,seg symbol mov es,ax mov bx,symbol 總是在'ES:BX'中載入一個指向符號'symbol'的有效指針。 而事情往往可能比這還要復雜些:因為16位的段與組是可以相互重疊的, 你通常可能需要通過不同的段基址,而不是首選的段基址來引用一個符 號,NASM可以讓你這樣做,通過使用'WRT'關鍵字,你可以這樣寫: mov ax,weird_seg ; weird_seg is a segment base mov es,ax mov bx,symbol wrt weird_seg 會在'ES:BX'中載入一個不同的,但功能上卻是相同的指向'symbol'的指 針。