核心結構
核心分為四個部分,分别是初始化代碼,核心代碼,核心資料段和公共例程段,主引導程式也是初始化代碼的組成部分。
- 初始化代碼用于從BIOS那裡接管處理器和計算機硬體的控制權,安裝最基本的段描述符,初始化最初的執行環境。然後,從硬碟上讀取和加載核心的剩餘部分,建立組成核心的各個記憶體段。
- 核心的代碼用于配置設定記憶體,讀取和加載使用者程式,控制使用者程式的執行。
- 核心資料段提供了一段可讀寫的記憶體空間,供核心自己使用。
- 公共例程用于提供各種用途和功能的子過程以簡化代碼的編寫。這些例程既可以用于核心,也供使用者程式調用。
除了以上的内容以外,核心檔案還包括一個頭部,記錄了各個段的彙編位置,這些統計資料用于告訴初始化代碼如何加載核心。
代碼清單
使用者程式
;代碼清單13-3
;檔案名:c13.asm
;檔案說明:使用者程式
;===============================================================================
SECTION header vstart=0
program_length dd program_end ;程式總長度#0x00
head_len dd header_end ;程式頭部的長度#0x04
stack_seg dd 0 ;用于接收堆棧段選擇子#0x08
stack_len dd 1 ;程式建議的堆棧大小#0x0c
;以4KB為機關
prgentry dd start ;程式入口#0x10
code_seg dd section.code.start ;代碼段位置#0x14
code_len dd code_end ;代碼段長度#0x18
data_seg dd section.data.start ;資料段位置#0x1c
data_len dd data_end ;資料段長度#0x20
;-------------------------------------------------------------------------------
;符号位址檢索表
salt_items dd (header_end-salt)/256 ;#0x24
salt: ;#0x28
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
header_end:
;===============================================================================
SECTION data vstart=0
buffer times 1024 db 0 ;緩沖區
message_1 db 0x0d,0x0a,0x0d,0x0a
db '**********User program is runing**********'
db 0x0d,0x0a,0
message_2 db ' Disk data:',0x0d,0x0a,0
data_end:
;===============================================================================
[bits 32]
;===============================================================================
SECTION code vstart=0
start:
mov eax,ds
mov fs,eax
mov eax,[stack_seg]
mov ss,eax
mov esp,0
mov eax,[data_seg]
mov ds,eax
mov ebx,message_1
call far [fs:PrintString]
mov eax,100 ;邏輯扇區号100
mov ebx,buffer ;緩沖區偏移位址
call far [fs:ReadDiskData] ;段間調用
mov ebx,message_2
call far [fs:PrintString]
mov ebx,buffer
call far [fs:PrintString] ;too.
jmp far [fs:TerminateProgram] ;将控制權傳回到系統
code_end:
;===============================================================================
SECTION trail
;-------------------------------------------------------------------------------
program_end:
保護模式微型核心程式
;代碼清單13-2
;檔案名:c13_core.asm
;檔案說明:保護模式微型核心程式
;以下常量定義部分。核心的大部分内容都應當固定
core_code_seg_sel equ 0x38 ;核心代碼段選擇子
core_data_seg_sel equ 0x30 ;核心資料段選擇子
sys_routine_seg_sel equ 0x28 ;系統公共例程代碼段的選擇子
video_ram_seg_sel equ 0x20 ;視訊顯示緩沖區的段選擇子
core_stack_seg_sel equ 0x18 ;核心堆棧段選擇子
mem_0_4_gb_seg_sel equ 0x08 ;整個0-4GB記憶體的段的選擇子
;-------------------------------------------------------------------------------
;以下是系統核心的頭部,用于加載核心程式
core_length dd core_end ;核心程式總長度#00
sys_routine_seg dd section.sys_routine.start
;系統公用例程段位置#04
core_data_seg dd section.core_data.start
;核心資料段位置#08
core_code_seg dd section.core_code.start
;核心代碼段位置#0c
core_entry dd start ;核心代碼段入口點#10
dw core_code_seg_sel
;===============================================================================
[bits 32]
;===============================================================================
SECTION sys_routine vstart=0 ;系統公共例程代碼段
;-------------------------------------------------------------------------------
;字元串顯示例程
put_string: ;顯示0終止的字元串并移動光标
;輸入:DS:EBX=串位址
push ecx
.getc:
mov cl,[ebx]
or cl,cl
jz .exit
call put_char
inc ebx
jmp .getc
.exit:
pop ecx
retf ;段間傳回
;-------------------------------------------------------------------------------
put_char: ;在目前光标處顯示一個字元,并推進
;光标。僅用于段内調用
;輸入:CL=字元ASCII碼
pushad
;以下取目前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
in al,dx ;高字
mov ah,al
dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
in al,dx ;低字
mov bx,ax ;BX=代表光标位置的16位數
cmp cl,0x0d ;回車符?
jnz .put_0a
mov ax,bx
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor
.put_0a:
cmp cl,0x0a ;換行符?
jnz .put_other
add bx,80
jmp .roll_screen
.put_other: ;正常顯示字元
push es
mov eax,video_ram_seg_sel ;0xb8000段的選擇子
mov es,eax
shl bx,1
mov [es:bx],cl
pop es
;以下将光标位置推進一個字元
shr bx,1
inc bx
.roll_screen:
cmp bx,2000 ;光标超出螢幕?滾屏
jl .set_cursor
push ds
push es
mov eax,video_ram_seg_sel
mov ds,eax
mov es,eax
cld
mov esi,0xa0 ;小心!32位模式下movsb/w/d
mov edi,0x00 ;使用的是esi/edi/ecx
mov ecx,1920
rep movsd
mov bx,3840 ;清除螢幕最底一行
mov ecx,80 ;32位程式應該使用ECX
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls
pop es
pop ds
mov bx,1920
.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
mov al,bh
out dx,al
dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
mov al,bl
out dx,al
popad
ret
;-------------------------------------------------------------------------------
read_hard_disk_0: ;從硬碟讀取一個邏輯扇區
;EAX=邏輯扇區号
;DS:EBX=目标緩沖區位址
;傳回:EBX=EBX+512
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al ;讀取的扇區數
inc dx ;0x1f3
pop eax
out dx,al ;LBA位址7~0
inc dx ;0x1f4
mov cl,8
shr eax,cl
out dx,al ;LBA位址15~8
inc dx ;0x1f5
shr eax,cl
out dx,al ;LBA位址23~16
inc dx ;0x1f6
shr eax,cl
or al,0xe0 ;第一硬碟 LBA位址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;讀指令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬碟已準備好資料傳輸
mov ecx,256 ;總共要讀取的字數
mov dx,0x1f0
.readw:
in ax,dx
mov [ebx],ax
add ebx,2
loop .readw
pop edx
pop ecx
pop eax
retf ;段間傳回
;-------------------------------------------------------------------------------
;彙編語言程式是極難一次成功,而且調試非常困難。這個例程可以提供幫助
put_hex_dword: ;在目前光标處以十六進制形式顯示
;一個雙字并推進光标
;輸入:EDX=要轉換并顯示的數
;輸出:無
pushad
push ds
mov ax,core_data_seg_sel ;切換到核心資料段
mov ds,ax
mov ebx,bin_hex ;指向核心資料段内的轉換表
mov ecx,8
.xlt:
rol edx,4
mov eax,edx
and eax,0x0000000f
xlat
push ecx
mov cl,al
call put_char
pop ecx
loop .xlt
pop ds
popad
retf
;-------------------------------------------------------------------------------
allocate_memory: ;配置設定記憶體
;輸入:ECX=希望配置設定的位元組數
;輸出:ECX=起始線性位址
push ds
push eax
push ebx
mov eax,core_data_seg_sel
mov ds,eax
mov eax,[ram_alloc]
add eax,ecx ;下一次配置設定時的起始位址
;這裡應當有檢測可用記憶體數量的指令
mov ecx,[ram_alloc] ;傳回配置設定的起始位址
mov ebx,eax
and ebx,0xfffffffc
add ebx,4 ;強制對齊
test eax,0x00000003 ;下次配置設定的起始位址最好是4位元組對齊
cmovnz eax,ebx ;如果沒有對齊,則強制對齊
mov [ram_alloc],eax ;下次從該位址配置設定記憶體
;cmovcc指令可以避免控制轉移
pop ebx
pop eax
pop ds
retf
;-------------------------------------------------------------------------------
set_up_gdt_descriptor: ;在GDT内安裝一個新的描述符
;輸入:EDX:EAX=描述符
;輸出:CX=描述符的選擇子
push eax
push ebx
push edx
push ds
push es
mov ebx,core_data_seg_sel ;切換到核心資料段
mov ds,ebx
sgdt [pgdt] ;以便開始處理GDT
mov ebx,mem_0_4_gb_seg_sel
mov es,ebx
movzx ebx,word [pgdt] ;GDT界限
inc bx ;GDT總位元組數,也是下一個描述符偏移
add ebx,[pgdt+2] ;下一個描述符的線性位址
mov [es:ebx],eax
mov [es:ebx+4],edx
add word [pgdt],8 ;增加一個描述符的大小
lgdt [pgdt] ;對GDT的更改生效
mov ax,[pgdt] ;得到GDT界限值
xor dx,dx
mov bx,8
div bx ;除以8,去掉餘數
mov cx,ax
shl cx,3 ;将索引号移到正确位置
pop es
pop ds
pop edx
pop ebx
pop eax
retf
;-------------------------------------------------------------------------------
make_seg_descriptor: ;構造存儲器和系統的段描述符
;輸入:EAX=線性基位址
; EBX=段界限
; ECX=屬性。各屬性位都在原始
; 位置,無關的位清零
;傳回:EDX:EAX=描述符
mov edx,eax
shl eax,16
or ax,bx ;描述符前32位(EAX)構造完畢
and edx,0xffff0000 ;清除基位址中無關的位
rol edx,8
bswap edx ;裝配基址的31~24和23~16 (80486+)
xor bx,bx
or edx,ebx ;裝配段界限的高4位
or edx,ecx ;裝配屬性
retf
;===============================================================================
SECTION core_data vstart=0 ;系統核心的資料段
;-------------------------------------------------------------------------------
pgdt dw 0 ;用于設定和修改GDT
dd 0
ram_alloc dd 0x00100000 ;下次配置設定記憶體時的起始位址
;符号位址檢索表
salt:
salt_1 db '@PrintString'
times 256-($-salt_1) db 0
dd put_string
dw sys_routine_seg_sel
salt_2 db '@ReadDiskData'
times 256-($-salt_2) db 0
dd read_hard_disk_0
dw sys_routine_seg_sel
salt_3 db '@PrintDwordAsHexString'
times 256-($-salt_3) db 0
dd put_hex_dword
dw sys_routine_seg_sel
salt_4 db '@TerminateProgram'
times 256-($-salt_4) db 0
dd return_point
dw core_code_seg_sel
salt_item_len equ $-salt_4
salt_items equ ($-salt)/salt_item_len
message_1 db ' If you seen this message,that means we '
db 'are now in protect mode,and the system '
db 'core is loaded,and the video display '
db 'routine works perfectly.',0x0d,0x0a,0
message_5 db ' Loading user program...',0
do_status db 'Done.',0x0d,0x0a,0
message_6 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
db ' User program terminated,control returned.',0
bin_hex db '0123456789ABCDEF'
;put_hex_dword子過程用的查找表
core_buf times 2048 db 0 ;核心用的緩沖區
esp_pointer dd 0 ;核心用來臨時儲存自己的棧指針
cpu_brnd0 db 0x0d,0x0a,' ',0
cpu_brand times 52 db 0
cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0
;===============================================================================
SECTION core_code vstart=0
;-------------------------------------------------------------------------------
load_relocate_program: ;加載并重定位使用者程式
;輸入:ESI=起始邏輯扇區号
;傳回:AX=指向使用者程式頭部的選擇子
push ebx
push ecx
push edx
push esi
push edi
push ds
push es
mov eax,core_data_seg_sel
mov ds,eax ;切換DS到核心資料段
mov eax,esi ;讀取程式頭部資料
mov ebx,core_buf
call sys_routine_seg_sel:read_hard_disk_0
;以下判斷整個程式有多大
mov eax,[core_buf] ;程式尺寸
mov ebx,eax
and ebx,0xfffffe00 ;使之512位元組對齊(能被512整除的數,
add ebx,512 ;低9位都為0
test eax,0x000001ff ;程式的大小正好是512的倍數嗎?
cmovnz eax,ebx ;不是。使用湊整的結果
mov ecx,eax ;實際需要申請的記憶體數量
call sys_routine_seg_sel:allocate_memory
mov ebx,ecx ;ebx -> 申請到的記憶體首位址
push ebx ;儲存該首位址
xor edx,edx
mov ecx,512
div ecx
mov ecx,eax ;總扇區數
mov eax,mem_0_4_gb_seg_sel ;切換DS到0-4GB的段
mov ds,eax
mov eax,esi ;起始扇區号
.b1:
call sys_routine_seg_sel:read_hard_disk_0
inc eax
loop .b1 ;循環讀,直到讀完整個使用者程式
;建立程式頭部段描述符
pop edi ;恢複程式裝載的首位址
mov eax,edi ;程式頭部起始線性位址
mov ebx,[edi+0x04] ;段長度
dec ebx ;段界限
mov ecx,0x00409200 ;位元組粒度的資料段描述符
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+0x04],cx
;建立程式代碼段描述符
mov eax,edi
add eax,[edi+0x14] ;代碼起始線性位址
mov ebx,[edi+0x18] ;段長度
dec ebx ;段界限
mov ecx,0x00409800 ;位元組粒度的代碼段描述符
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+0x14],cx
;建立程式資料段描述符
mov eax,edi
add eax,[edi+0x1c] ;資料段起始線性位址
mov ebx,[edi+0x20] ;段長度
dec ebx ;段界限
mov ecx,0x00409200 ;位元組粒度的資料段描述符
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+0x1c],cx
;建立程式堆棧段描述符
mov ecx,[edi+0x0c] ;4KB的倍率
mov ebx,0x000fffff
sub ebx,ecx ;得到段界限
mov eax,4096
mul dword [edi+0x0c]
mov ecx,eax ;準備為堆棧配置設定記憶體
call sys_routine_seg_sel:allocate_memory
add eax,ecx ;得到堆棧的高端實體位址
mov ecx,0x00c09600 ;4KB粒度的堆棧段描述符
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+0x08],cx
;重定位SALT
mov eax,[edi+0x04]
mov es,eax ;es -> 使用者程式頭部
mov eax,core_data_seg_sel
mov ds,eax
cld
mov ecx,[es:0x24] ;使用者程式的SALT條目數
mov edi,0x28 ;使用者程式内的SALT位于頭部内0x2c處
.b2:
push ecx
push edi
mov ecx,salt_items
mov esi,salt
.b3:
push edi
push esi
push ecx
mov ecx,64 ;檢索表中,每條目的比較次數
repe cmpsd ;每次比較4位元組
jnz .b4
mov eax,[esi] ;若比對,esi恰好指向其後的位址資料
mov [es:edi-256],eax ;将字元串改寫成偏移位址
mov ax,[esi+4]
mov [es:edi-252],ax ;以及段選擇子
.b4:
pop ecx
pop esi
add esi,salt_item_len
pop edi ;從頭比較
loop .b3
pop edi
add edi,256
pop ecx
loop .b2
mov ax,[es:0x04]
pop es ;恢複到調用此過程前的es段
pop ds ;恢複到調用此過程前的ds段
pop edi
pop esi
pop edx
pop ecx
pop ebx
ret
;-------------------------------------------------------------------------------
start:
mov ecx,core_data_seg_sel ;使ds指向核心資料段
mov ds,ecx
mov ebx,message_1
call sys_routine_seg_sel:put_string
;顯示處理器品牌資訊
mov eax,0x80000002
cpuid
mov [cpu_brand + 0x00],eax
mov [cpu_brand + 0x04],ebx
mov [cpu_brand + 0x08],ecx
mov [cpu_brand + 0x0c],edx
mov eax,0x80000003
cpuid
mov [cpu_brand + 0x10],eax
mov [cpu_brand + 0x14],ebx
mov [cpu_brand + 0x18],ecx
mov [cpu_brand + 0x1c],edx
mov eax,0x80000004
cpuid
mov [cpu_brand + 0x20],eax
mov [cpu_brand + 0x24],ebx
mov [cpu_brand + 0x28],ecx
mov [cpu_brand + 0x2c],edx
mov ebx,cpu_brnd0
call sys_routine_seg_sel:put_string
mov ebx,cpu_brand
call sys_routine_seg_sel:put_string
mov ebx,cpu_brnd1
call sys_routine_seg_sel:put_string
mov ebx,message_5
call sys_routine_seg_sel:put_string
mov esi,50 ;使用者程式位于邏輯50扇區
call load_relocate_program
mov ebx,do_status
call sys_routine_seg_sel:put_string
mov [esp_pointer],esp ;臨時儲存堆棧指針
mov ds,ax
jmp far [0x10] ;控制權交給使用者程式(入口點)
;堆棧可能切換
return_point: ;使用者程式傳回點
mov eax,core_data_seg_sel ;使ds指向核心資料段
mov ds,eax
mov eax,core_stack_seg_sel ;切換回核心自己的堆棧
mov ss,eax
mov esp,[esp_pointer]
mov ebx,message_6
call sys_routine_seg_sel:put_string
;這裡可以放置清除使用者程式各種描述符的指令
;也可以加載并啟動其它程式
hlt
;===============================================================================
SECTION core_trail
;-------------------------------------------------------------------------------
core_end:
硬碟主引導扇區代碼
;代碼清單13-1
;檔案名:c13_mbr.asm
;檔案說明:硬碟主引導扇區代碼
;建立日期:2011-10-28 22:35 ;設定堆棧段和棧指針
core_base_address equ 0x00040000 ;常數,核心加載的起始記憶體位址
core_start_sector equ 0x00000001 ;常數,核心的起始邏輯扇區号
mov ax,cs
mov ss,ax
mov sp,0x7c00
;計算GDT所在的邏輯段位址
mov eax,[cs:pgdt+0x7c00+0x02] ;GDT的32位實體位址
xor edx,edx
mov ebx,16
div ebx ;分解成16位邏輯位址
mov ds,eax ;令DS指向該段以進行操作
mov ebx,edx ;段内起始偏移位址
;跳過0#号描述符的槽位
;建立1#描述符,這是一個資料段,對應0~4GB的線性位址空間
mov dword [ebx+0x08],0x0000ffff ;基位址為0,段界限為0xFFFFF
mov dword [ebx+0x0c],0x00cf9200 ;粒度為4KB,存儲器段描述符
;建立保護模式下初始代碼段描述符
mov dword [ebx+0x10],0x7c0001ff ;基位址為0x00007c00,界限0x1FF
mov dword [ebx+0x14],0x00409800 ;粒度為1個位元組,代碼段描述符
;建立保護模式下的堆棧段描述符 ;基位址為0x00007C00,界限0xFFFFE
mov dword [ebx+0x18],0x7c00fffe ;粒度為4KB
mov dword [ebx+0x1c],0x00cf9600
;建立保護模式下的顯示緩沖區描述符
mov dword [ebx+0x20],0x80007fff ;基位址為0x000B8000,界限0x07FFF
mov dword [ebx+0x24],0x0040920b ;粒度為位元組
;初始化描述符表寄存器GDTR
mov word [cs: pgdt+0x7c00],39 ;描述符表的界限
lgdt [cs: pgdt+0x7c00]
in al,0x92 ;南橋晶片内的端口
or al,0000_0010B
out 0x92,al ;打開A20
cli ;中斷機制尚未工作
mov eax,cr0
or eax,1
mov cr0,eax ;設定PE位
;以下進入保護模式... ...
jmp dword 0x0010:flush ;16位的描述符選擇子:32位偏移
;清流水線并串行化處理器
[bits 32]
flush:
mov eax,0x0008 ;加載資料段(0..4GB)選擇子
mov ds,eax
mov eax,0x0018 ;加載堆棧段選擇子
mov ss,eax
xor esp,esp ;堆棧指針 <- 0
;以下加載系統核心程式
mov edi,core_base_address
mov eax,core_start_sector
mov ebx,edi ;起始位址
call read_hard_disk_0 ;以下讀取程式的起始部分(一個扇區)
;以下判斷整個程式有多大
mov eax,[edi] ;核心程式尺寸
xor edx,edx
mov ecx,512 ;512位元組每扇區
div ecx
or edx,edx
jnz @1 ;未除盡,是以結果比實際扇區數少1
dec eax ;已經讀了一個扇區,扇區總數減1
@1:
or eax,eax ;考慮實際長度≤512個位元組的情況
jz setup ;EAX=0 ?
;讀取剩餘的扇區
mov ecx,eax ;32位模式下的LOOP使用ECX
mov eax,core_start_sector
inc eax ;從下一個邏輯扇區接着讀
@2:
call read_hard_disk_0
inc eax
loop @2 ;循環讀,直到讀完整個核心
setup:
mov esi,[0x7c00+pgdt+0x02] ;不可以在代碼段内尋址pgdt,但可以
;通過4GB的段來通路
;建立公用例程段描述符
mov eax,[edi+0x04] ;公用例程代碼段起始彙編位址
mov ebx,[edi+0x08] ;核心資料段彙編位址
sub ebx,eax
dec ebx ;公用例程段界限
add eax,edi ;公用例程段基位址
mov ecx,0x00409800 ;位元組粒度的代碼段描述符
call make_gdt_descriptor
mov [esi+0x28],eax
mov [esi+0x2c],edx
;建立核心資料段描述符
mov eax,[edi+0x08] ;核心資料段起始彙編位址
mov ebx,[edi+0x0c] ;核心代碼段彙編位址
sub ebx,eax
dec ebx ;核心資料段界限
add eax,edi ;核心資料段基位址
mov ecx,0x00409200 ;位元組粒度的資料段描述符
call make_gdt_descriptor
mov [esi+0x30],eax
mov [esi+0x34],edx
;建立核心代碼段描述符
mov eax,[edi+0x0c] ;核心代碼段起始彙編位址
mov ebx,[edi+0x00] ;程式總長度
sub ebx,eax
dec ebx ;核心代碼段界限
add eax,edi ;核心代碼段基位址
mov ecx,0x00409800 ;位元組粒度的代碼段描述符
call make_gdt_descriptor
mov [esi+0x38],eax
mov [esi+0x3c],edx
mov word [0x7c00+pgdt],63 ;描述符表的界限
lgdt [0x7c00+pgdt]
jmp far [edi+0x10]
;-------------------------------------------------------------------------------
read_hard_disk_0: ;從硬碟讀取一個邏輯扇區
;EAX=邏輯扇區号
;DS:EBX=目标緩沖區位址
;傳回:EBX=EBX+512
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al ;讀取的扇區數
inc dx ;0x1f3
pop eax
out dx,al ;LBA位址7~0
inc dx ;0x1f4
mov cl,8
shr eax,cl
out dx,al ;LBA位址15~8
inc dx ;0x1f5
shr eax,cl
out dx,al ;LBA位址23~16
inc dx ;0x1f6
shr eax,cl
or al,0xe0 ;第一硬碟 LBA位址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;讀指令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬碟已準備好資料傳輸
mov ecx,256 ;總共要讀取的字數
mov dx,0x1f0
.readw:
in ax,dx
mov [ebx],ax
add ebx,2
loop .readw
pop edx
pop ecx
pop eax
ret
;-------------------------------------------------------------------------------
make_gdt_descriptor: ;構造描述符
;輸入:EAX=線性基位址
; EBX=段界限
; ECX=屬性(各屬性位都在原始
; 位置,其它沒用到的位置0)
;傳回:EDX:EAX=完整的描述符
mov edx,eax
shl eax,16
or ax,bx ;描述符前32位(EAX)構造完畢
and edx,0xffff0000 ;清除基位址中無關的位
rol edx,8
bswap edx ;裝配基址的31~24和23~16 (80486+)
xor bx,bx
or edx,ebx ;裝配段界限的高4位
or edx,ecx ;裝配屬性
ret
;-------------------------------------------------------------------------------
pgdt dw 0
dd 0x00007e00 ;GDT的實體位址
;-------------------------------------------------------------------------------
times 510-($-$$) db 0
db 0x55,0xaa