首先,我們先調整一下程式的位置,以節省空間(怕以後某天我們的Boot Loader會爆)。並且還要記得把16位元的程式和32位元的程式分開(用.code16或.code32設定)。
我們主要要利用VGA映射的記憶體,來控制螢幕。這個記憶體在0xB8000,每2Bytes代表螢幕上的一個字,第一個Byte是文字,第二個是背景和字的顏色。只用這些東西,就可以做很多事情了。不過,我們為了將東西印在游標上,並且再將游標移到下一個位置,所以我們要利用VGA的控制Port來控制。但是有點複雜,我們就參考https://wiki.osdev.org/Text_Mode_Cursor#Without_the_BIOS裡面的方法吧!
首先,我們先取得游標位置。
getCursorPos:
movb $0x0E, %al
movw $0x3D4, %dx
outb %al, %dx
incw %dx
inb %dx, %al
shlw $8, %ax
movb $0x0F, %al
decw %dx
outb %al, %dx
incw %dx
inb %dx, %al
ret
我們先透過傳入0x0E至Port號碼是0x3D4的索引暫存器以後,再讀入0x3D5,就可以得到高8bit的游標位置。再將0x0F用同樣方法後,就可以得到低8bit的位置。再來利用很接近的方法,將由bx暫存器傳入的新游標位置,輸出到VGA控制器。
setCursorPos:
movb $0x0F, %al
movw $0x3D4, %dx
outb %al, %dx
movb %bl, %al
incw %dx
outb %al, %dx
movb $0x0E, %al
decw %dx
outb %al, %dx
movb %bh, %al
incw %dx
outb %al, %dx
ret
最後,就可以透過這兩個函式,實作一個簡易的輸出函式了。print32:
call getCursorPos
movw %ax, %dx
# Count String
xorl %ecx, %ecx
movl %esi, %edi
cnt32:
movb (%edi), %al
cmpb $0, %al
je cnt32_exit
incl %edi
incl %ecx
jmp cnt32
cnt32_exit:
# Print
movl $0xb8000, %edi
andl $0xFFFF, %edx
shll %edx
addl %edx, %edi
shrl %edx
print32loop:
movb (%esi), %al
movb %al, (%edi)
movb $0x07, 1(%edi)
incl %esi
addl $2, %edi
loop print32loop
# Move cursor to next line
movw %dx, %bx
addw $80, %bx
call setCursorPos
ret
但還有一件事情要記得,我們進入保護模式後,要先把所有的區段描述暫存器準備好,像是這樣。
movw $0x10, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
記得,現在我們的每一個東西都要的是從0x0數起來的位置了,所以需要很注意。現在,我們的程式碼長這樣,已經很長了。
.code16
.section .text
# Init all the registers
xorw %ax, %ax
xorw %bx, %bx
xorw %cx, %cx
xorw %dx, %dx
movw $0x7c0, %ax
movw %ax, %es
movw %ax, %ds
movw %ax, %ss
# Reset Floppy (int 0x13, ah = 0x0, dl = 0 (first floppy))
movb $0, %ah
int $0x13
# Read Number 2 (CHS)
# (int 0x13, ah = 0x2, al = 1, ch = 0, cl = 2, dh = 0, dl = 0, ES:BX = 0x7E00)
movw $0x200, %bx
movb $0x2, %ah
movb $0x1, %al
movb $0x0, %ch
movb $0x2, %cl
movb $0x0, %dh
movb $0x0, %dl
int $0x13
jc error
# Print
movw $HELLO_MESSAGE, %bp
call print
call getRam
# Clear Interrupt Flag
cli
# A20
inb $0x92, %al
orb $2, %al
outb %al, $0x92
# LGDT
lgdt GDTR
# Enter Protected Mode
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmp $0x08, $(0x7c00+OS_PMODE)
.code32
OS_PMODE:
movw $0x10, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movl $(0x7c00+PMODE_MESSAGE), %esi
call print32
jmp .
.code16
# ====================================================================
# Print Error Message
# ====================================================================
error:
# Print Error
movw $ERROR_MSG, %bp
call print
jmp .
# ====================================================================
# print function
# ====================================================================
# es:bp as Message position, zero-term string
print:
# Get cursor position (Return dh = row, dl = column ...)
movb $0x03, %ah
movb $0, %bh
int $0x10
# Count String
xorw %cx, %cx
movw %bp, %si
cnt:
movb (%si), %ah
cmpb $0, %ah
je cnt_exit
incw %si
incw %cx
jmp cnt
cnt_exit:
# Print
# (ah = 0x13, al = 0x0, bh = 0, bl = 7, cx = strlen, dh:dl=row:column, es:bp=7c0:Message)
movw $0x1300, %ax
movw $0x7, %bx
int $0x10
# Move cursor to next line
incb %dh
xorb %dl, %dl
movb $0, %bh
movb $0x02, %ah
int $0x10
ret
#=====================================================================
#=====================================================================
# int 0xE820
# (eax = 0xe820) (ebx = 0) (es:di = smap) (ecx = buffer size = 0x200) (edx = smap)
#=====================================================================
getRam:
movl $0xe820, %eax
xorl %ebx, %ebx
movw $0x400, %di
movl $32, %ecx
movl $0x534D4150, %edx
getRam_loop:
int $0x15
movl %eax, %edx
movl $0xe820, %eax
movl $32, %ecx
addw $32, %di
cmpl $0, %ebx
jne getRam_loop
ret
.code32
# =====================================================================
# print32 (32 bits print method)
# =====================================================================
# esi as Message position, zero-term string
print32:
call getCursorPos
movw %ax, %dx
# Count String
xorl %ecx, %ecx
movl %esi, %edi
cnt32:
movb (%edi), %al
cmpb $0, %al
je cnt32_exit
incl %edi
incl %ecx
jmp cnt32
cnt32_exit:
# Print
movl $0xb8000, %edi
andl $0xFFFF, %edx
shll %edx
addl %edx, %edi
shrl %edx
print32loop:
movb (%esi), %al
movb %al, (%edi)
movb $0x07, 1(%edi)
incl %esi
addl $2, %edi
loop print32loop
# Move cursor to next line
movw %dx, %bx
addw $80, %bx
call setCursorPos
ret
# =====================================================================
# getCursorPos
# =====================================================================
getCursorPos:
movb $0x0E, %al
movw $0x3D4, %dx
outb %al, %dx
incw %dx
inb %dx, %al
shlw $8, %ax
movb $0x0F, %al
decw %dx
outb %al, %dx
incw %dx
inb %dx, %al
ret
# =====================================================================
# setCursorPos (bx = Position)
# =====================================================================
setCursorPos:
movb $0x0F, %al
movw $0x3D4, %dx
outb %al, %dx
movb %bl, %al
incw %dx
outb %al, %dx
movb $0x0E, %al
decw %dx
outb %al, %dx
movb %bh, %al
incw %dx
outb %al, %dx
ret
HELLO_MESSAGE:
.asciz "Hello world!"
PMODE_MESSAGE:
.asciz "We're in Protected Mode Now!!"
ERROR_MSG:
.asciz "READ FLOPPY ERROR"
GDT_NUL:
.long 0
.long 0
GDT_CODE32:
.long 0x0000FFFF
.long 0x00CF9A00
GDT_DATA32:
.long 0x0000FFFF
.long 0x00CF9200
GDTR:
.word 24
.long GDT_NUL+0x7c00
.org 0x1FE, 0x62
.byte 0x55
.byte 0xAA
.org 0x400, 0x62
# 接SuperBlock
用QEMU的螢幕截圖:
👉【幫我們一個忙!】👈
👋如果您喜歡這篇文章,請在下方按5個Like!
❤您的支持是我們最大的動力!
您只要登入帳號(Facebook、Google),在下方按5個Like,我們就會收到來自LikeCoin基金會的贊助。
您只需要支持我們,完全不會花到錢!