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

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

1.实模式显示字符

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

如何对屏幕输出字符?很简单,这个模式的缓冲区地址为0xb8000(0xb8000-0xb8f9f),屏幕上一个字符的这里占两个字节,第一个字节前高4位说明这个字符的背景色,低4位说明字符的颜色,第二个字节是ascii码字符。因此总共占用的内存为80*25*2=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进字符串地址,字符数,背景色前景色
STRING equ 0x18 ;字符串地址
SIZE equ 0x16;字符数量
COLOR equ 0x14;背景色前景色
print:
	pusha
	push bp
	mov bp,sp
	mov ax,0xb800
	mov es,ax

	mov di,word [bp+STRING]	;di字符串地址
	mov cx,word [bp+SIZE]	;cx字符数
	mov dx,word [bp+COLOR]	;颜色
	call get_pos
outstr:
	mov dl,[di]
	call judeg
	inc di
	loop outstr
end_print:
	pop bp
	popa
	ret
;-------------------------------------------------------
judeg:
judge_back:	;判断退格
	cmp dl,0x08
	jz ch_back
	jmp judge_tab
ch_back:
	cmp ax,0
	jle next
	dec ax
	call set_pos
	imul bx,ax,2
	mov word [es:bx],0
	jmp next
;-------------------------------------------------------
judge_tab:	;判断制表符
	cmp dl,0x09
	jz ch_tab
	jmp judge_enter
ch_tab:
	mov dl,8
	mov bx,ax
	div dl
	mov al,8
	sub al,ah
	xor ah,ah
	add bx,ax
	mov ax,bx
	call set_pos
	jmp next
;-------------------------------------------------------
judge_enter:	;判断回车
	cmp dl,0x0d
	jz ch_enter
	jmp judge_newline
ch_enter:
	push dx
	mov bx,ax
	mov dl,80
	div dl	;ax储存商,dx储存余数
	pop dx
	shr ax,8
	sub bx,ax
	mov ax,bx
	call set_pos
	jmp next
;--------------------------------------------------------
judge_newline:	;判断换行
	cmp dl,0x0a
	jz ch_newline
	jmp judge_ch
ch_newline:
	cmp ax,1840
	jae calup
	jmp n1
calup:
	call roll_up
	jmp next
n1:
	add ax,80
	call set_pos
	jmp next
;---------------------------------------------------------
judge_ch:		;判断是否为可输出字符,小于0x20,大于0x7e不可输出
	cmp dl,0x20
	jl next
	cmp dl,0x7e
	ja next
mov_cursor:
	inc ax
	call set_pos
outch:
	mov bx,ax
	dec bx
	imul bx,2
	mov word [es:bx],dx
next:
	ret
;-----------------------------------------------------------
;向上滚动一行
roll_up: 
	push ds
	push es
	push cx
	push di
	mov bx,0xb800
	mov ds,bx
	mov bx,0xb7f6
	mov es,bx
	xor di,di
	xor si,si
	mov cx,2000  ;共传送80*25字
	rep movsw
	pop di
	pop cx
	pop es
	pop ds
	ret

调用方法例:

push msg       ;msg为字符串首地址
push 20        ;字符数
push 0x2f00    ;颜色设定为绿底白字
call print

msg db "hello,world!"

结果:

下面是背景色、前景色说明

背景色

前景色

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来返回。

猜你喜欢

转载自blog.csdn.net/lindorx/article/details/83957903