1 显卡和显存
1.1 显卡和显存
先搞清楚如下几个概念:
显卡: 为显示器提供内容。
显示器: 将显卡所提供的内容呈现在屏幕上。
集成显卡: 集成在主板上,和主板是一体的。
独立显卡: 独立生产和销售的一个独立的部件。
显示存储器: 简称“显存”,每个显卡都有自己的存储器。
1.2 显存的显示模式
图形模式:
文本模式:
实际上还包含属性字节:
属性字节的含义:
1.3 8086中显存的地址
2 控制显卡显示内容
2.1 需求分析
我们需要在屏幕上显示一个标号的地址,显示内容类似:“label offset:256”。
显示标号的步骤如下:
2.2 汇编代码实现
汇编代码如下:
mov ax,0xb800
;将b800传送到ax寄存器中
mov es,ax
;将ax寄存器中的值,也就是b800传送到附加段寄存器es中
;为什么不写成 mov es,b800 ?
;因为intel处理器不允许,它只允许:mov 段寄存器,通用寄存器/内存单元
;b800这个数是干什么用的?---显存的段地址
;-----------------------------------------------------------------
mov byte [es:0x00],'L'
;将"字符L的ASCII码"存储到显存的第1个存储单元中
;mov byte [es:0x00],0100 1100b / 76 / 0x4C
;地址必须使用方括号括起来
;byte/word:明确的告诉编译器这条指令的数据宽度
;不需要修饰:Mov [00],AL / Mov AX,[00]
mov byte [es:0x01],0x07
;将"字符L的属性"存储到显存的第2个存储单元中
;属性值07:黑底白字、无闪烁、无加亮
mov byte [es:0x02],'a'
mov byte [es:0x03],0x07
mov byte [es:0x04],'b'
mov byte [es:0x05],0x07
mov byte [es:0x06],'e'
mov byte [es:0x07],0x07
mov byte [es:0x08],'l'
mov byte [es:0x09],0x07
mov byte [es:0x0a],' '
mov byte [es:0x0b],0x07
mov byte [es:0x0c],"o"
mov byte [es:0x0d],0x07
mov byte [es:0x0e],'f'
mov byte [es:0x0f],0x07
mov byte [es:0x10],'f'
mov byte [es:0x11],0x07
mov byte [es:0x12],'s'
mov byte [es:0x13],0x07
mov byte [es:0x14],'e'
mov byte [es:0x15],0x07
mov byte [es:0x16],'t'
mov byte [es:0x17],0x07
mov byte [es:0x18],':'
mov byte [es:0x19],0x07
;-----------------------------------------------------------------
mov ax,number
;将"标号"存储到ax寄存器中,作为下面除法运算的:被除数的低16位
;编译器在编译的时候会将number替换为其所对应的的汇编地址
mov dx,0
;将0存储到dx寄存器中,作为下面除法运算的:被除数的高16位
mov bx,10
;将10,也就是二进制的1010存储到bx寄存器中,作为下面除法运算的:除数
;以上三条指令为第2个步骤的除法运算做好了准备工作
;-----------------------------------------------------------------
mov cx,cs
;将代码段寄存器cs中的值,也就是0x000,传送到cx寄存器中
mov ds,cx
;将cx寄存器中的值,也就是0x000传送到数据段寄存器ds中
;ds寄存器中存储的是本程序在内存中的段地址
;-----------------------------------------------------------------
div bx
;对应第1次除法运算
;商存储在ax寄存器中,作为下次运算的被除数
;余数,也就是标号的个位上的值存储在dx寄存器中
mov [0x7c00+number+0x00],dl
;将第1次除法运算所得到的标号个位上的值
;存入了我们使用db所声明的第1个存储单元中
xor dx,dx
;异或指令:对两个数进行异或运算,并将结果存储到目的操作数中
;异或运算的特点:相同为0,不同为1
;这条指令的作用:将dx寄存器中的值清零
;为第2次除法运算做准备
div bx
;对应第2次除法运算
mov [0x7c00+number+0x01],dl
;将第2次除法运算所得到的标号十位上的值
;存入了我们使用db所声明的第2个存储单元中
xor dx,dx
;为第3次除法运算做准备
div bx
;对应第3次除法运算
mov [0x7c00+number+0x02],dl
;将第3次除法运算所得到的标号百位上的值
;存入了我们使用db所声明的第3个存储单元中
;-----------------------------------------------------------------
mov al,[0x7c00+number+0x02]
;将标号百位上的数值传送给al寄存器
add al,0x30
;获得标号百位上数值的ASCII码
mov [es:0x1a],al
;将标号百位上数值的ASCII码存入显存
mov byte [es:0x1b],0x04
;将0x04存入显存中偏移地址为0x1b的存储单元
;字符属性04:黑底、红字、无闪烁、无加亮
mov al,[0x7c00+number+0x01]
;将标号十位上的数值传送给al寄存器
add al,0x30
;获得标号十位上数值的ASCII码
mov [es:0x1c],al
;将标号十位上数值的ASCII码存入显存
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x00]
;将标号个位上的数值传送给al寄存器
add al,0x30
;获得标号个位上数值的ASCII码
mov [es:0x1e],al
;将标号个位上数值的ASCII码存入显存
mov byte [es:0x1f],0x04
mov byte [es:0x20],'D'
;将字符D的ASCII码存入显存
mov byte [es:0x21],0x07
;字符属性07:黑底白字、无闪烁、无加亮
;-----------------------------------------------------------------
infi jmp near infi
;无限循环
;-----------------------------------------------------------------
number db 0,0,0,0,0
;标号:number = 汇编地址 = 0x0100
;书写规范:以字母开头
;db:声明并初始化数据
;声明数据的本质就是:在内存中占用一块空间
;初始化数据的本质就是:给这个空间赋予一个值
;在内存中占用了5个字节的空间,这5个字节的值都是0
;-----------------------------------------------------------------
times 249 db 0
;让编译器重复生成db 0 ---- 249次
db 0x55,0xaa
;硬盘主引导扇区的有效标志
2.3 更精简的代码实现
jmp near start
;跳过下面的数据区
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0,0
;--------------------------------------------------------------------
start:
mov ax,0x07c0
mov ds,ax
mov ax,0xb800
mov es,ax
;--------------------------------------------------------------------
cld
;作用:将方向标志位清0 :正向
;std指令:将方向标志位置1:反向
mov si,mytext
;所要传送的数据,它的偏移地址
mov di,0
;所要传送的位置,它的偏移地址
mov cx,(number-mytext)/2
;所要传送的次数
rep movsw
;--------------------------------------------------------------------
mov ax,number
;AX:被除数的低16位
;目的:让它参与运算,以得到标号各个数位上的值
mov bx,ax
;目的,将来存储计算所得到的标号各个数位上的值
mov cx,5
;运算的次数
mov si,10
;除数
;--------------------------------------------------------------------
digit:
xor dx,dx
div si
mov [bx],dl
;[bx]:指向了第2条DB指令所声明的第1个存储单元
inc bx
;将bx寄存器中的值+1
;目的:下一个循环中,让[bx]指向第2条DB指令所声明的第2个存储单元
;Dec:将bx寄存器中的值-1
loop digit
;循环
;--------------------------------------------------------------------
mov bx,number
;bx:基址寄存器
mov si,4
;SI:源变址寄存器
show:
mov al,[bx+si]
;第1次循环,将标号万位上的值存储到了al寄存器中
;第2次循环,将标号千位上的值存储到了al寄存器中
add al,0x30
;获取标号各个数位值的ASCII码
mov ah,0x04
;0x04:字符属性
mov [es:di],ax
;将标号的ASCII码及其属性存入显存中
;第1次循环-->万位 第2次循环-->千位
add di,2
;为我们后面的循环,存储标号数位上值的ASCII码及其属性做准备
dec si
;为我们后面的循环,访问标号各个数位的值做准备
jns show
;jns:条件转移指令 SF=0跳转 SF=1不跳转
;--------------------------------------------------------------------
mov word [es:di],0x0744
;高字节07:字符属性 低字节44:字符D的ASCII码
jmp near $
; == $ jmp near $
;执行效果 == infi jmp near infi
;$:当做标号来用
times 510-($-$$) db 0
; == $ times 510-($-$$) db 0
;$ = 当前指令的汇编地址
;$$ = 当前段($$所在的段)的起始汇编地址
;510-($-$$) = 为保证0x55和0xaa的位置,所需要添加的字节数
db 0x55,0xaa
参考资料: