x86实模式下字符的显示及中断

454 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

参考书籍《0x86从实模式到保护模式》

1.实模式显示字符

首先,电脑开机进入系统,加载bios,然后bios完成一些硬件的初始化,从磁盘读取mbr到绝对地址0x7c00处,然后跳转至0x7c00,此时屏幕的显示模式默认为80*25(一行80个字符,共25行)。

如何对屏幕输出字符?很简单,这个模式的缓冲区地址为0xb8000(0xb8000-0xb8f9f),屏幕上一个字符的这里占两个字节,第一个字节前高4位说明这个字符的背景色,低4位说明字符的颜色,第二个字节是ascii码字符。因此总共占用的内存为80252=4000字节。光知道如何显示字符还不行,还需要知道光标并可以设置光标的位置,光标储存在显卡内的两个8位寄存器内,可以利用端口来读取这个两个寄存器,首先,将寄存器索引存入0x03d4,这两个寄存器的索引号分别为0xe和0xf:

例:
mov dx,0x3d4
mov al,0xe
out dx,al

通过0x03d5来读取相应寄存器的值:

例:
mov dx,0x3d5
in al,dx

完整获取光标位置的代码如下:

;获取光标位置,储存在ax中
get_pos:
push dx
push bx
mov al,0x0e
mov dx,0x03d4
out dx,al
mov dx,0x03d5
in al,dx
mov bl,al
 
mov al,0x0f
mov dx,0x3d4
out dx,al
mov dx,0x03d5
in al,dx
mov ah,bl
pop bx
pop dx
ret

要注意,ax是作为一个整体的值,并非ah存行数,al存列数。简单讲,ax*2+0xb8000指向这个字符在内存中的位置。设定光标位置很简单,在0x3d4存入索引值后,有out指令向0x3d5写入光标的位置:

例:
;修改光标位置
set_pos:
push dx
push bx
mov bx,ax
 
mov dx,0x03d4
mov al,0xe
out dx,al
 
mov dx,0x03d5
mov al,bh
out dx,al
 
mov dx,0x03d4
mov al,0x0f
out dx,al
 
mov dx,0x03d5
mov al,bl
out dx,al
 
mov ax,bx
pop bx
pop dx
ret

之后我们要做的就是根据光标的位置,向内存写入正确的字符:

push msg       ;msg为字符串首地址
push 20        ;字符数
push 0x2f00    ;颜色设定为绿底白字
call print
 
msg db "hello,world!"

结果:

20181111151231120.png 下面是背景色、前景色说明:

背景色前景色
0=黑色0=黑色
1=蓝色1=蓝色
2=绿色2=绿色
3=青色3=青色
4=红色4=红色
5=紫红5=紫红
6=橙色6=橙色
7=浅灰7=浅灰
8=黑色+闪烁8=深灰
9=蓝色+闪烁9=紫色
a=绿色+闪烁a=亮绿
b=青色+闪烁b=亮青
c=红色+闪烁c=亮红
d=紫红+闪烁d=亮紫红
e=橙色+闪烁e=亮黄
f=白色+闪烁f=白色

2.实模式下的中断

实模式下的中断非常简单,中断向量表存放在0x0-0x3ff之间,每4字节为一个表项,每个表项的值指向一段程序的入口,这段程序就是处理中断的程序。例如,使用了int 0指令,cpu就会进入0x0~0x3指向的地址,然后执行这里的程序。地址的表示使用小端字节序,前两个字节为偏移地址,后两个地址为段地址。比如地址0起始的4个字节为0x53,0xff,0x00,0xf0,段地址为0xff00,偏移地址为0xff53,实际地址就是0xf000*0x10+0xff53=0xfff53。这里就是说明使用int 0指令后会转向起始地址为0xfff53的程序。

编写中断程序时要注意,调用中断时,cpu会向栈中依次压入flag标志位,cs,ip,要用iret来返回。