1. 程式人生 > >真實模式下字元的顯示及中斷

真實模式下字元的顯示及中斷

參考書籍《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來返回。