匯編:有關在屏幕區顯示字符的四種方法


    李忠老師的《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 中斷來操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM