前言
通過BIOS提供的中斷,我們的MBR程式在螢屏上輸出了綠油油的 Hi from MBR!,但只有在 實模式 下,我們才可以使用BIOS中斷,而我們要撰寫的作業系統是運行在32位 保護模式 下的程式,既然不能呼叫BIOS中斷了,那么我們就直接和顯卡對話吧,
外部硬體與CPU
忙碌的CPU要和大量的硬體打交道,處理大量由硬體傳遞的資料,不同的硬體傳遞的資料是不一樣的、回應速度是不一樣的,而CPU的時間又是那么的寶貴,不能被浪費,所以硬體工程師們指定了一系列的IO介面,作為CPU與硬體之間的中間人,首先,IO介面用有緩沖區,在一定程度上緩解了CPU與硬體設備回應速度不匹配;其次,IO介面還擁有處理資料格式的功能,減少CPU的作業量,
還有些需要CPU頻繁向其傳輸資料的硬體(如:顯卡),直接將自身的記憶體空間,映射到計算機記憶體的某塊空間上,只要在特定的地址上填寫資料,就相當于在該硬體的記憶體上填寫了資料,
和顯卡對話
顯存分布
現在,我們知道顯卡將自己部分的記憶體空間映射到可計算機的記憶體中,想要和顯卡對話,我們就要知道這塊映射空間在哪塊記憶體中(其實顯卡也有自己的IO介面,不過我們不涉及),

因為我們想要在螢屏上顯示文字,所以我們選擇將資料發送到起始地址為 b8000 的適用于 文本模式 的顯存,
資料格式
在 文本模式 下,表示一個字符需要兩個位元組,第一個位元組表示 字符ASCII碼,第二個位元組表示 字符屬性 ,

因為可表示顏色有限,所以有一份整理好的字符屬性表可供大家參考,

改進MBR
知道了怎么和顯卡對話,我們現在就可以來改進之前撰寫的 MBR 了,
初始化段暫存器 gs
因為實模式下CPU的尋址方式為 段基地址:偏移地址,即 物理地址 = 段基地址 * 16 + 偏移地址,而文字模式顯存起始地址為 b8000,所以我們將 gs 暫存器的值初始化為 0xb800,即:
mov ax, 0xb800
mov gs, ax
撰寫 putchar 函式
將 di 暫存器初始化為 0,通過 gs:di 尋址,所以 putchar 函式:
; al: ASCII碼
; bl: 字符屬性
putchar:
mov [gs:di], al ; 放置ASCII碼
inc di ; di++
mov [gs:di], bl ; 放置字符屬性
inc di
ret ; return
撰寫 print_message 函式
我們將要列印的字串 Hi from MBR!,通過 message db "Hi from MBR!" db 0 的方式儲存,其中 message 就相當于一個 char *,0 代表字串的結束符,
所以我們可以通過 si 暫存器保存 message,即 字串的起始地址,通過 inc si 移動指標,通過 [si] 對指標解參考得到字符,通過 putchar 函式輸出字符,通過判斷字符是否為 0 從而判斷是否到達字串末尾,所以 print_message 函式:
print_message:
.init:
mov ax, message
mov si, ax ; 讓 ds:si 指向 message 的開頭
mov bl, 0x07 ; 字符屬性:黑底白字
.print:
mov al, [si] ; 讀取字串
or al, 0 ; al | 0
jz .end ; 若為0,則輸出完畢
call putchar ; 不為0,則輸出 al
inc si ; si++
jmp .print ; 回圈
.end:
jmp $
MBR總覽
點擊查看代碼
; 主引導程式 mbr.s
SECTION MBR vstart=0x7c00
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov ax, 0xb800
mov gs, ax
mov sp, 0x7c00 ; 初始化堆疊指標
xor di, di ; di = 0
; 目前 0x7c00 以下暫時是安全的區域,就把它當作找來用
; push ax; ax的內容壓堆疊保存,(sp)=(sp)-2
; 清屏: 利用 0x06 號功能, 上卷全部行, 則可清屏
; -----------------------------------------------------------
; INT 0x10 功能號: 0x06 功能描述: 上卷視窗
; -----------------------------------------------------------
; 輸入:
; ah: 功能號
; al: 上卷的行數
; bh: 上卷行屬性
; (cl, ch): 視窗左上角的 (X, Y) 位置
; (dl, dh): 視窗右上角的 (X, Y) 位置
; 無回傳值
mov ax, 0x0600
mov bx, 0x0700
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80, 25)
; VGA文本模式中, 一行只能容納 80 個字符, 共 25 行
; 下標從 0 開始, 所以 0x18=24,0x4f=79
int 0x10
print_message:
.init:
mov ax, message
mov si, ax ; 讓 ds:si 指向 message 的開頭
mov bl, 0x07 ; 字符屬性:黑底白字
.print:
mov al, [si] ; 讀取字串
or al, 0 ; al | 0
jz .end ; 若為0,則輸出完畢
call putchar ; 不為0,則輸出 al
inc si ; si++
jmp .print ; 回圈
.end:
jmp $
; al: ASCII碼
; bl: 字符屬性
putchar:
mov [gs:di], al ; 放置ASCII碼
inc di ; di++
mov [gs:di], bl ; 放置字符屬性
inc di
ret ; return
message db "Hi from MBR"
db 0
times 510-($-$$) db 0 ; 以 0 填補空間至 510 位元組處
db 0x55, 0xaa ; 魔數 0x55 0xaa

可以看見,我們成功輸出了字串 Hi from MBR!,但游標的位置并不對勁,下一篇博客,我們來做一個屬于自己的游標,并撰寫一個函式來監聽鍵盤輸入,并將鍵盤的輸入列印到螢屏上!敬請期待(手動狗頭),
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/303205.html
標籤:Linux
下一篇:在自己的專案中使用PCL
