• 汇编:有关在屏幕区显示字符的四种方法


        李忠老师的《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 中断来操作。

  • 相关阅读:
    做一点,记一点 ~ JQuery EasyUI使用小结(注意点)
    做一点,记一点 ~ Java调用Oracle存储过程
    Linux中定时执行DB2的存储过程
    Apache POI -- Java 导出Excel文档(笔记)
    一篇很不错的学习Flex的入门级教程
    Java的String和StringBuffer和StringBuilder详解
    Java操作properties文件
    认识WebService
    J2EE安全策略:为tomcat页面设置访问权限(j_security_check)
    MyEclipse 2014常用设置
  • 原文地址:https://www.cnblogs.com/liugl7/p/4418900.html
Copyright © 2020-2023  润新知