李忠老師的《x86匯編語言:從實模式到保護模式》中第五章到第七章的部分,每一章在講述知識點的同時,分別使用了三種不同的顯示字符的方法,加上調用BIOS的10h中 斷的方法,這里做出一次簡單梳理:
一:第五章,最基礎的直接用mov 的方法
代碼如下:
1 ;代碼清單5-1 2 ;文件名:c05_mbr.asm 3 ;文件說明:硬盤主引導扇區代碼 4 ;創建日期:2011-3-31 21:15 5 6 mov ax,0xb800 ;指向文本模式的顯示緩沖區 7 mov es,ax 8 9 ;以下顯示字符串"Label offset:" 10 mov byte [es:0x00],'L' 11 mov byte [es:0x01],0x07 12 mov byte [es:0x02],'a' 13 mov byte [es:0x03],0x07 14 mov byte [es:0x04],'b' 15 mov byte [es:0x05],0x07 16 mov byte [es:0x06],'e' 17 mov byte [es:0x07],0x07 18 mov byte [es:0x08],'l' 19 mov byte [es:0x09],0x07 20 mov byte [es:0x0a],' ' 21 mov byte [es:0x0b],0x07 22 mov byte [es:0x0c],"o" 23 mov byte [es:0x0d],0x07 24 mov byte [es:0x0e],'f' 25 mov byte [es:0x0f],0x07 26 mov byte [es:0x10],'f' 27 mov byte [es:0x11],0x07 28 mov byte [es:0x12],'s' 29 mov byte [es:0x13],0x07 30 mov byte [es:0x14],'e' 31 mov byte [es:0x15],0x07 32 mov byte [es:0x16],'t' 33 mov byte [es:0x17],0x07 34 mov byte [es:0x18],':' 35 mov byte [es:0x19],0x07 36 37 mov ax,number ;取得標號number的偏移地址 38 mov bx,10 39 40 ;設置數據段的基地址 41 mov cx,cs 42 mov ds,cx 43 44 ;求個位上的數字 45 mov dx,0 46 div bx 47 mov [0x7c00+number+0x00],dl ;保存個位上的數字 48 49 ;求十位上的數字 50 xor dx,dx 51 div bx 52 mov [0x7c00+number+0x01],dl ;保存十位上的數字 53 54 ;求百位上的數字 55 xor dx,dx 56 div bx 57 mov [0x7c00+number+0x02],dl ;保存百位上的數字 58 59 ;求千位上的數字 60 xor dx,dx 61 div bx 62 mov [0x7c00+number+0x03],dl ;保存千位上的數字 63 64 ;求萬位上的數字 65 xor dx,dx 66 div bx 67 mov [0x7c00+number+0x04],dl ;保存萬位上的數字 68 69 ;以下用十進制顯示標號的偏移地址 70 mov al,[0x7c00+number+0x04] 71 add al,0x30 72 mov [es:0x1a],al 73 mov byte [es:0x1b],0x04 74 75 mov al,[0x7c00+number+0x03] 76 add al,0x30 77 mov [es:0x1c],al 78 mov byte [es:0x1d],0x04 79 80 mov al,[0x7c00+number+0x02] 81 add al,0x30 82 mov [es:0x1e],al 83 mov byte [es:0x1f],0x04 84 85 mov al,[0x7c00+number+0x01] 86 add al,0x30 87 mov [es:0x20],al 88 mov byte [es:0x21],0x04 89 90 mov al,[0x7c00+number+0x00] 91 add al,0x30 92 mov [es:0x22],al 93 mov byte [es:0x23],0x04 94 95 mov byte [es:0x24],'D' 96 mov byte [es:0x25],0x07 97 98 infi: jmp near infi ;無限循環 99 100 number db 0,0,0,0,0 101 102 times 203 db 0 103 db 0x55,0xaa
這里采用的最基礎的做法,就是對字符進行一個一個的處理。先將顯示緩存區的地址0xb800賦給es寄存器,然后通過 mov byte[es:0x00],'L' 的形式,來處理后續的字符。這種方法較為簡單,這里不再贅述。
二:第六章,采用了批量處理的方法
代碼如下:
1 ;代碼清單6-1 2 ;文件名:c06_mbr.asm 3 ;文件說明:硬盤主引導扇區代碼 4 ;創建日期:2011-4-12 22:12 5 6 jmp near start 7 8 mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\ 9 'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07 10 number db 0,0,0,0,0 11 12 start: 13 mov ax,0x7c0 ;設置數據段基地址 14 mov ds,ax 15 16 mov ax,0xb800 ;設置附加段基地址 17 mov es,ax 18 19 cld 20 mov si,mytext 21 mov di,0 22 mov cx,(number-mytext)/2 ;實際上等於 13 23 rep movsw 24 25 ;得到標號所代表的偏移地址 26 mov ax,number 27 28 ;計算各個數位 29 mov bx,ax 30 mov cx,5 ;循環次數 31 mov si,10 ;除數 32 digit: 33 xor dx,dx 34 div si 35 mov [bx],dl ;保存數位 36 inc bx 37 loop digit 38 39 ;顯示各個數位 40 mov bx,number 41 mov si,4 42 show: 43 mov al,[bx+si] 44 add al,0x30 45 mov ah,0x04 46 mov [es:di],ax 47 add di,2 48 dec si 49 jns show 50 51 mov word [es:di],0x0744 52 53 jmp near $ 54 55 times 510-($-$$) db 0 56 db 0x55,0xaa
這里采用的辦法是批量傳送,后續用loop循環挨個處理。這樣的寫法明顯比上一種寫法要高明一些,減少了工作量。這段代碼中值得注意的地方是 mov si,mytext (其中mytext是聲明的字符的地址),這里值得留意的原因之一是在做顯示時間的編碼中,有過下列這樣的寫法,所以會格外的留心。
1 org 7c00h 2 start1: 3 4 5 mov ax, cs ; 置其他段寄存器值與CS相同 6 mov ds, ax ; 數據段 7 mov es, ax 8 9 mov bl, 10h 10 mov bp, Message1 11 12 mov ah, 02h 13 int 1ah 14 15 xor ax, ax 16 mov al, ch 17 div bl 18 add al, 0x30 19 mov [es:bp+2], al 20 add ah, 0x30 21 mov [es:bp+3], ah 22 23 xor ax, ax 24 mov al, cl 25 div bl 26 add al, 0x30 27 mov [es:bp+5], al 28 add ah, 0x30 29 mov [es:bp+6], ah 30 31 xor ax, ax 32 mov al, dh 33 div bl 34 add al, 0x30 35 mov [es:bp+8], al 36 add ah, 0x30 37 mov [es:bp+9], ah 38 39 mov dh, 3 40 mov dl, 0 41 mov ax, 1301h ; 功能號 42 mov bp, Message1 43 mov cx, MessageLength1 44 mov bx, 0007h 45 int 10h 46 47 ; ret 48 49 Message1: 50 db ' 00:00:00' 51 MessageLength1 equ ($-Message1) 52 53 times 510-($-$$) db 0 ; 用0填充引導扇區剩下的空間 54 db 55h, 0aah ; 引導扇區結束標志
(上面的那段代碼的功能是調用BIOS中斷顯示系統時間)這段代碼中對於“00:00:00”的處理方法,代碼二中批量處理si處的mytext字段有異曲同工之妙,這里mark一下。
關於代碼二中顯示數字的方法,是用到了loop循環。先將數字按照“除以10”的方法得到每一位的值,然后將其加上0x30(有關ASCII的知識可解釋這一點是為什么),然后將最終值賦予 依次遞增的顯存地址對應的內容,直到將之前處理的每一位數字都顯示出來,over.
三:第七章,使用棧來操作
這一章的代碼的特殊之處在於通過將字符串按照一個一個的順序分別取到之后,將其按照順序壓棧,然后再依次出棧再處理而顯示。
1 ;代碼清單7-1 2 jmp near start 3 4 message db '1+2+3+...+100=' 5 6 start: 7 mov ax,0x7c0 ;設置數據段的段基地址 8 mov ds,ax 9 10 mov ax,0xb800 ;設置附加段基址到顯示緩沖區 11 mov es,ax 12 13 ;以下顯示字符串 14 mov si,message 15 mov di,0 16 mov cx,start-message 17 @g: 18 mov al,[si] 19 mov [es:di],al 20 inc di 21 mov byte [es:di],0x07 22 inc di 23 inc si 24 loop @g 25 26 ;以下計算1到100的和 27 xor ax,ax 28 mov cx,1 29 @f: 30 add ax,cx 31 inc cx 32 cmp cx,100 33 jle @f 34 35 ;以下計算累加和的每個數位 36 xor cx,cx ;設置堆棧段的段基地址 37 mov ss,cx 38 mov sp,cx 39 40 mov bx,10 41 xor cx,cx 42 @d: 43 inc cx 44 xor dx,dx 45 div bx 46 or dl,0x30 47 push dx 48 cmp ax,0 49 jne @d 50 51 ;以下顯示各個數位 52 @a: 53 pop dx 54 mov [es:di],dl 55 inc di 56 mov byte [es:di],0x07 57 inc di 58 loop @a 59 60 jmp near $ 61 62 63 times 510-($-$$) db 0 64 db 0x55,0xaa
對於代碼段四,第一部分顯示“1+2+3+4+...+100=”的部分是沿用了上面的代碼二中的做法,使用loop循環處理。
而下面處理數字的部分,是一種新的處理方式。這里是將數字依次“除以10”得到每一位的數之后,將其加上0x00(原因:ASCII顯示字符需要)壓入棧中,然后在下一個循環中,依次出棧並且處理使得其能夠顯示出來。
四:調用BIOS的10h中斷來顯示字符
以上,無論是最簡單的mov的做法,還是movbw的做法,異或壓棧出棧的做法,都難免分別處理每一個字符的圈子。這里介紹一種調用BIOS中斷的做法,直接處理一串字符串,較為簡單,可參考性高。
1 org 07c00h ; 告訴編譯器程序加載到 7c00處 2 mov ax, cs 3 mov ds, ax 4 mov es, ax 5 call DispStr ; 調用顯示字符串例程 6 jmp $ ; 無限循環 7 8 DispStr: 9 mov ax, BootMessage 10 mov bp, ax ; es:bp = 串地址 11 mov cx, 16 ; cx = 串長度 12 mov ax, 01301h ; ah = 13, al = 01h 13 mov bx, 000ch ; 頁號為 0(bh = 0) 黑底紅字(bl = 0Ch,高亮) 14 mov dl, 0 15 int 10h ; 10h 號中斷 16 ret 17 18 BootMessage: 19 db "Hello, OS world!" 20 times 510-($-$$) db 0 ; 填充剩下的空間,使生成的二進制代碼恰好為 21 dw 0xaa55 ; 結束標志
這里的做法是調用BIOS的10h中斷來顯示“Hello,OS world!”,其中bp為字符串地址,cx為串長度,ah為功能號,al指示光標置於串尾,bx指示頁號為0然后字符顯示屬性為黑底紅字,dh為行號,dl為列號(如果不做處理的話,默認dh,dl皆為0,即在第0行第0列顯示),參數設置完之后則調用10h中斷顯示字符串。
總結:以上的四種方法,通過學習不僅了解顯示的方法,更重要的是對匯編語言有了更多的認識。以上方法在實際操作中介於方便與否,大多采用的直接調用BIOS的10h 中斷來操作。
