x86汇编语言之显存操控屏幕输出

1,405 阅读5分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」。

操控显存输出字符串

前面咱们介绍过使用中断的方式输出字符串, 今天我们学习一种不使用中断的方式实现字符串的打印

在8086的内存地址结构中,B8000H~BFFFFH这部分的内存区域为显存区域,一旦向这个地址空间写入数据,cpu会从0号偏移地址开始读取数据然后显示输出, (每写入一次数据就从0开始读取一次)

代码尝试:

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
end start

在这块区域中,每个字符固定占用两个字节的空间,也就是ds:[0]ds:[1]存放一个字符的信息,前者存放字符具体的内容,后者存放字符对应的颜色

比如:

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,00000100B ;让字符以红色显示          
	   mov ds:[1],dl
end start

字符颜色的设置规则:

0 0 0 0 0 0 0 0  ;用8个二进制位表示字符属性

从高往低数,第一个二进制位表示是否显示闪烁痕迹

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,10000000B ;保留字符闪烁痕迹       
	   mov ds:[1],dl
end start

第234个二进制位表示字符背景颜色 分别代表:RGB,即red、green、blue

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,01000000B ;背景颜色设为红色       
	   mov ds:[1],dl
end start

第5个二进制位表示字符是否高亮

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,00001100B ;字符颜色设置为红色 并且高亮显示 
	   mov ds:[1],dl
end start

第678个二进制位表示字符本身的颜色 分别代表:RGB,即red、green、blue

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,00000111B ;背景颜色设为白色  系统默认颜色是白色  
	   mov ds:[1],dl
end start

由于cpu会从0号偏移地址开始读取数据然后显示输出,因此假如你直接在6号偏移地址写入字符数据, 那么前面三个数据会以占位形式存在

start:
	   mov ax,0B800H
	   mov ds,ax
  
	 
	   mov dl,'a'           
	   mov ds:[0],dl
	   
	   mov dl,00000111B  
	   mov ds:[6],dl ;输出结果为"   a"
end start

字符串打印

data segment
	str db 'hello pangshu'
	endstr db ''
data ends

code segment

    start:                  
    
           mov ax,data
           mov ds,ax
           mov ax,0B800H
           mov es,ax

		   mov cx ,offset endstr-str
		   mov bx,0 
		   mov si,0      
		   
    print:
           mov dl,ds:[si]           
           mov es:[bx],dl

           mov dl,00000111B ;背景颜色设为白色  系统默认颜色是白色  
           mov es:[bx+1],dl  
           inc si
           add bx,2
           loop print

code ends
end start

借助字符不断刷新显示的特性,可用让字符动画显示

;让字符从左往右移动
code segment

    start:                        
           mov ax,0B800H
           mov es,ax
           
		   mov bx,0 
   		   mov cx,30
    print:     
           mov es:[bx],' ' 
           mov dl,'a'        
           mov es:[bx+2],dl        
           add bx,2    
           loop print

code ends
end start

屏幕默认显示80x25个字符,全屏显示106x38个字符,那么可以根据这个特性,让字符上下移动

;让字符从上往下移动
code segment

    start:                        
           mov ax,0B800H
           mov es,ax
           
		   mov bx,0 
   		   mov cx,25
    print:     
           mov es:[bx],'a' 
           mov dl,' '        
           mov es:[bx-160],dl        
           add bx,160   ;为什么是160而不是80 以内一个字符占两个字节的空间, 80个字符总共偏移了160
           loop print

code ends
end start



;让字符从下往上移动
code segment

    start:                        
           mov ax,0B800H
           mov es,ax
           
		   mov bx,160*24 
   		   mov cx,25
    print:     
           mov es:[bx],'a' 
           mov dl,' '        
           mov es:[bx+160],dl        
           sub bx,160   ;为什么是160而不是80 以内一个字符占两个字节的空间, 80个字符总共偏移了160
           loop print

code ends
end start

另外, 让字符斜着移动

;让让字符斜着移动
code segment

    start:                        
           mov ax,0B800H
           mov es,ax
           
		   mov bx,0 
   		   mov cx,25
    print:     
           mov es:[bx],'a' 
           mov dl,' '        
           mov es:[bx-161],dl        
           add bx,161   ;向右斜加偏移量 向左斜减偏移量
           loop print

code ends
end start

补充: 在8086中系统提供了一个显示服务(Video Service)中断供我们使用,使用10H这个中断码也可以打印带有颜色属性的字符串

;示例1:
mov ah,2 ;放置光标
mov bh,0 ;第0页
mov dh,5 ;行号
mod dl,12 ;列号
int 10H


;示例2:
mov ah,9 ;在光标的位置显示字符
mov al,'a' ;字符
mov bl,11001010B ;颜色
mov bh,0 ;第0页
mov cx,3 ;重复显示3次
int 10H

使用键盘输入控制字符移动

使用16号中断码

;使用键盘控制字符移动
code segment

    start:                        
           mov ax,0B800H
           mov es,ax
		   mov bx,0 
   		   mov cx,30    
   		      
   		scan:   
   		   mov ah, 00H
   		   int 16H       
   		   cmp al,61H ;判断两个值是否相等
   		   jne scan2  ;jmp not equal 如果两者不相等 则跳转到scan2 否则往下执行
   		   call  left 
   		   jmp scan    
   	    scan2:	     
   		   cmp al,64H
   		   jne scan3    
   		   call  right
   		   jmp scan    
   	    scan3:	   
   		   cmp al,77H
   		   jne scan4 
   		   call top
   		   jmp scan   
   	    scan4:	   
   		   
   		   cmp al,73H
   		   jne scan 
   		   
   		   call  down
   		   jmp scan    
   		   
    right:     
           mov es:[bx],' ' 
           mov dl,'a'        
           mov es:[bx+2],dl        
           add bx,2    
          ret   
     
   		   
    left:     
           mov es:[bx],' ' 
           mov dl,'a'        
           mov es:[bx-2],dl        
           sub bx,2    
          ret  
           
    top:     
           mov es:[bx],' ' 
           mov dl,'a'        
           mov es:[bx-160],dl        
           sub bx,160    
          ret  
           
     
   		   
    down:     
           mov es:[bx],' ' 
           mov dl,'a'        
           mov es:[bx+160],dl        
           add bx,160    
          ret     

code ends
end start