mbr.asm 没有什么改变
%include "boot.inc"
section MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
;使用 int 0x10 功能号 0x06 清屏
mov ah,0x06
mov al,0x00 ;上卷行数 0->全部
mov bl,0x00
mov bh,0x07
mov cx,0x0000 ;左上( 0, 0)
mov dx,0x184f ;右下(80,25)
int 0x10
; ---------- 在显存中输入信息 ---------
; 输出背景为 黄 色,前景 红 色,并且跳动的字符串 "HELLO WORLD"
mov bx,0
mov cx,14
mov si,0
print:
mov al,[s+si]
mov [gs:bx],al
inc bx
mov byte [gs:bx],0x24
inc bx
add si,1
loop print
mov eax, LOADER_START_SECTOR
mov bx, LOADER_BASE_ADDR
mov cx, 4
call rd_disk_m_16
jmp LOADER_BASE_ADDR+0x300
rd_disk_m_16:
mov esi,eax
mov di,cx
;指定扇区数0x1f2 8bit
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
;LBA 0-7 8bit
mov dx,0x1f3
out dx,al
;LBA 8-15 8bit
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA 16-23
shr eax,cl
mov dx,0x1f5
out dx,al
;LBA Device | 24-27
shr eax,cl
and al,0000_1111b
or al,1110_0000b ;LBA, DEV主盘
mov dx,0x1f6
out dx,al
;写入命令
mov dx,0x1f7
mov al,0x20
out dx,al
;检测硬盘状态
.not_ready:
nop ;相当于个停顿
in al,dx
and al,1000_1000b ;忙/准备好
cmp al,0000_1000b
jnz .not_ready
;读数据
mov ax,di
mov dx,256 ;512/2
mul dx
mov cx,ax ;一共ax个字节
mov dx,0x1f0
.read_data:
in ax,dx
mov [bx],ax
add bx,2
loop .read_data
ret
s db "1 MBR_start!!!"
times 510 - ($-$$) db 0
db 0x55, 0xaa
loader
- 增加了读硬盘32位模式
- 将内核从硬盘中读到内存中
- 将内存中的kernel中的段 cpy到指定的虚拟地址去
- 通过跳转指令进入内核
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
;jmp loader_start
GDT_BASE: dd 0x00000000
dd 0x00000000
CODE_DESC: dd 0x0000FFFF
dd DESC_CODE_HIGH4
DATA_STACK_DESC: dd 0x0000FFFF
dd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
times 60 dq 0 ;预留60个描述符的位子
;选择子
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
total_mem_bytes dd 0 ;4 保存内存容量
;gdt指针
gdt_ptr dw GDT_LIMIT ;6
dd GDT_BASE
ards_buf times 244 db 0 ;244 ards结构体缓存
ards_nr dw 0 ;2 ards结构体数量,一共 4+6+244+2 = 256字节
loader_start:
;==== 读取内存数
;int 15h eax,ebx,es:di,ecx,edx
xor ebx,ebx ;ARDS后续值,第一次为0
mov edx,0x534d4150 ;固定为签名标记
mov di,ards_buf ;ards结构缓冲区
.e820_mem_get_loop:
mov eax,0x0000e820
mov ecx,20 ;ards 地址范围描述符结构是20字节
int 0x15
jc .e820_failed_so_try_e801 ;若cf为1,则发生错误,常识0xe801
add di,cx ;cx是读到的结构缓冲区中数据的字节数
inc word [ards_nr]
cmp ebx,0 ;如果是0说明这是最后一个ARDS结构
jnz .e820_mem_get_loop
;找出(base_add_low + length_low)的最大值,也就是内存容量
mov cx,[ards_nr]
mov ebx,ards_buf
xor edx,edx
.find_max_mem_area:
mov eax,[ebx]
add eax,[ebx+8]
add ebx,20 ;ebx存放 ARDS缓存内容地址
cmp edx,eax ;比较 edx 与 eax大小
jge .next_ards
mov edx,eax
.next_ards:
loop .find_max_mem_area
jmp .mem_get_ok
;0x15 0xe801
.e820_failed_so_try_e801:
mov ax,0xe801
int 0x15
jc .e801_failed_so_try88
;1 先算出低15MB的内存
mov cx,0x400;ax KB单位2^10 ,0x400-> 1024
mul cx
shl edx,16
and eax,0x0000FFFF
or edx,eax
add edx, 0x100000 ;补上1MB
mov esi,edx ;先放在esi中
;2 算>16MB的内存 bx 64KB为单位
xor eax,eax
mov ax,bx
mov ecx,0x10000 ;将64KB单位转为byte
mul ecx
add esi,eax ;<=15 + >16
jmp .mem_get_ok
.e801_failed_so_try88:
mov ah,0x88
int 0x15
jc .error_hlt
and eax,0x0000FFFF ;ax*1024+1MB
mov cx,0x400
mul cx ;ax*1024->dx,ax
shl edx,16
or edx,eax
add edx,0x100000 ;+1MB
.mem_get_ok:
mov [total_mem_bytes],edx
.error_hlt:
mov byte [gs:160],'2'
mov byte [gs:162],' '
mov byte [gs:164],'0'
mov byte [gs:166],'x'
mov byte [gs:168],'1'
mov byte [gs:170],'5'
;====
;1 打开 A20
in al,0x92
or al,0x02
out 0x92,al
;2 加载 GDT
lgdt [gdt_ptr]
;3 cr0 pe置1
mov eax,cr0
or eax,0x01
mov cr0,eax
jmp dword SELECTOR_CODE:p_mode_start
[bits 32]
p_mode_start:
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
mov ax,SELECTOR_VIDEO
mov gs,ax
mov byte [gs:320],'3'
mov byte [gs:322],' '
mov byte [gs:324],'P'
mov byte [gs:326],'r'
mov byte [gs:328],'o'
mov byte [gs:330],'t'
mov byte [gs:332],'e'
mov byte [gs:334],'c'
mov byte [gs:336],'t'
mov byte [gs:338],'i'
mov byte [gs:340],'o'
mov byte [gs:342],'n'
mov byte [gs:344],' '
mov byte [gs:346],'m'
mov byte [gs:348],'o'
mov byte [gs:350],'d'
mov byte [gs:352],'e'
mov byte [gs:354],'.'
;---读取硬盘---
mov eax,KERNEL_START_SECTOR
mov ebx,KERNEL_BIN_BASE_ADDR
mov ecx,200
call rd_disk_m_32
;---创建页表---
call setup_page
sgdt[gdt_ptr]
mov ebx,[gdt_ptr+2] ;ebx = GDT_BASE
or dword [ebx+0x18+4],0xc0000000 ; GDT_BASE+3*8+4,到了VIDEO的高4字节
add dword [gdt_ptr+2],0xc0000000; 为之后的高系统区1GB映射做准备
add esp,0xc0000000
mov eax,PAGE_DIR_TABLE_POS
mov cr3,eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax ;开启cr0 31位
lgdt [gdt_ptr] ;重新加载GDT
mov byte [gs:480],'4'
mov byte [gs:482],' '
mov byte [gs:484],'P'
mov byte [gs:486],'D'
mov byte [gs:488],'E'
;--- 强刷流水线 进入内核 ---
jmp SELECTOR_CODE:enter_kernel
enter_kernel:
call kernel_init
mov esp,0xc009f000
mov byte [gs:640],'5'
mov byte [gs:642],' '
mov byte [gs:644],'K'
jmp KERNEL_ENTRY_POINT
;----- 创建页目录及页表 -----
setup_page:
mov ecx,4096 ;1024*4 1024个页目录项
mov esi,0
.clear_page_dir:
mov byte [PAGE_DIR_TABLE_POS+esi],0
inc esi
loop .clear_page_dir
.create_pde:
mov eax,PAGE_DIR_TABLE_POS
add eax,0x1000
mov ebx,eax
or eax,PG_US_U | PG_RW_W | PG_P
mov [PAGE_DIR_TABLE_POS+0x0],eax
mov [PAGE_DIR_TABLE_POS+0xc00],eax
sub eax, 0x1000
mov [PAGE_DIR_TABLE_POS+4092],eax ;将目录页首地址放在最后一个目录项中
;创建页表项
mov ecx,256
mov esi,0
mov edx,PG_US_U | PG_RW_W | PG_P
.create_pte:
mov [ebx+esi*4],edx
add edx,4096
inc esi
loop .create_pte
;创建高1GB中的其他页目录项
mov eax,PAGE_DIR_TABLE_POS
add eax,0x2000
or eax, PG_US_U | PG_RW_W | PG_P
mov ebx, PAGE_DIR_TABLE_POS
mov ecx,254
mov esi,769
;768是1G中的第一个目录项
.create_kernel_pde:
mov [ebx+esi*4],eax
inc esi
add eax,0x1000
loop .create_kernel_pde
ret
;----- kernel.bin 中的 segment 拷贝到编译的地址
kernel_init:
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
mov dx,[KERNEL_BIN_BASE_ADDR+42] ;program header大小
mov ebx,[KERNEL_BIN_BASE_ADDR+28];program header偏移量
add ebx,KERNEL_BIN_BASE_ADDR
mov cx,[KERNEL_BIN_BASE_ADDR+44] ;program header数量
.each_segment:
cmp byte [ebx+0],PT_NULL
je .PTNULL
;memcpy(dst, src, size)
push dword[ebx+16];size
mov eax, [ebx+4]
add eax, KERNEL_BIN_BASE_ADDR
push eax ;src 源地址
push dword [ebx+8] ; dst 目的地址
call mem_cpy
add esp,12
.PTNULL:
add ebx,edx
loop .each_segment
ret
;------ mem_cpy (dst, src, size) 逐字节cpy---
mem_cpy:
cld
push ebp
mov ebp,esp
push ecx
mov edi,[ebp+8]
mov esi,[ebp+12]
mov ecx,[ebp+16]
rep movsb
pop ecx
pop ebp
ret
;--- rd_disk_m_32 ---
rd_disk_m_32:
mov esi,eax
mov di,cx
;指定扇区数0x1f2 8bit
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
;LBA 0-7 8bit
mov dx,0x1f3
out dx,al
;LBA 8-15 8bit
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA 16-23
shr eax,cl
mov dx,0x1f5
out dx,al
;LBA Device | 24-27
shr eax,cl
and al,0000_1111b
or al,1110_0000b ;LBA, DEV主盘
mov dx,0x1f6
out dx,al
;写入命令
mov dx,0x1f7
mov al,0x20
out dx,al
;检测硬盘状态
.not_ready_32:
nop ;相当于个停顿
in al,dx
and al,1000_1000b ;忙/准备好
cmp al,0000_1000b
jnz .not_ready_32
;读数据
mov ax,di
mov dx,256 ;512/2
mul dx
mov cx,ax ;一共ax个字节
mov dx,0x1f0
.read_data_32:
in ax,dx
mov [ebx],ax
add ebx,2
loop .read_data_32
ret
boot.inc
- 增加了内核部分的4条宏定义
; loader and kernel
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
; gdt 属性
DESC_G_4K equ 0x800000
DESC_D_32 equ 0x400000
DESC_L equ 0x000000 ;是否开启64位代码
DESC_AVL equ 0x000000 ;可用的
DESC_LIMIT_CODE2 equ 0xF0000
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0x00000
DESC_P equ 0x8000
DESC_DPL_0 equ 0x0000
DESC_DPL_1 equ 0x2000
DESC_DPL_2 equ 0x4000
DESC_DPL_3 equ 0x6000
DESC_S_CODE equ 0x1000
DESC_S_DATA equ DESC_S_CODE ;相同的
DESC_S_SYS equ 0x0000
DESC_TYPE_CODE equ 0x800 ;x1 e0 w0 a0
DESC_TYPE_DATA equ 0x200 ;x0 e0 w1 a0
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x0b
;选择子 属性
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
;------ loader and kernel ------
PAGE_DIR_TABLE_POS equ 0x100000
PG_P equ 1b
PG_RW_R equ 00b
PG_RW_W equ 10b
PG_US_S equ 000b
PG_US_U equ 100b
;---kernel ---
KERNEL_START_SECTOR equ 0x9
KERNEL_BIN_BASE_ADDR equ 0x70000
PT_NULL equ 0
KERNEL_ENTRY_POINT equ 0xc0001500
kernel.bin编译,还是因为gcc 编译太大了,所以用clang编译。同时还要注意,编译环境可能是64位下,所以要指定编译后的代码为 32 位的。
clang -m32 -c -o main.o main.c && ld -m elf_i386 main.o -Ttext 0xc0001500 -e main -o kernel.bin && dd if=kernel.bin of=hd60M.img bs=512 count=200 seek=9 conv=notrunc
运行结果:
- MBR运行
- 获取内存容量
- 进入32位安全模式
- 进入页表分页
- 加载内核并运行
2024/2/24日记录
loader.asm,这里的mbr.asm中已经jmp到了指定地点+0x3000,其他没有改变什么,所以,GDT在宏定义的位置。我要说明的就只有这一点。
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
GDT_BASE:
dd 0x00000000
dd 0x00000000
CODE_DESC:
dd 0x0000FFFF
dd DESC_CODE_HIGH4
DATA_STACK_DESC:
dd 0x0000FFFF
dd DESC_DATA_HIGH4
VIDEO_DESC:
dd 0x80000007
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
times 60 dq 0;预留60个描述符的空位
;选择子
SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0
;gdt 指针
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
;============= 读取内存数
total_mem_bytes dd 0;4 保存内存容量
ards_buf times 244 db 0
ards_nr dw 0
jmp loader_start
def_read_card_num:
xor ebx,ebx
mov edx,0x534d4150
mov di,ards_buf
.e820_mem_get_loop:
mov eax,0x0000e820
mov ecx,20
int 0x15
jc .e820_failed_so_try_e801
add di,cx
inc word[ards_nr]
cmp ebx,0
jnz .e820_mem_get_loop
;找出(base_add_low + length_low)的最大值,也就是内存容量
mov cx,[ards_nr]
mov ebx,ards_buf
xor edx,edx
.find_max_mem_area:
mov eax,[ebx]
add eax,[ebx+8]
add ebx,20
cmp edx,eax
jge .next_ards
mov edx,eax
.next_ards:
loop .find_max_mem_area
jmp .mem_get_ok
;0x15 0xe801
.e820_failed_so_try_e801:
mov ax,0xe801
int 0x15
jc .e801_failed_so_try88
;1 先算出低15MB的内存
mov cx,0x400 ;ax KB单位2^10, 0x400->1024
mul cx
shl edx,16
and eax,0x0000FFFF
or edx,eax
add edx,0x100000
mov esi,edx
;2 算 > 16MB 的内存 bx 64KB位单位
xor eax,eax
mov ax,bx
mov ecx,0x10000;将64KB单位转为byte
mul ecx
add esi,eax ; <=15 + >16
jmp .mem_get_ok
.e801_failed_so_try88:
mov ah,0x88
int 0x15
jc .error_hlt
and eax,0x0000FFFF
mov cx,0x400
mul cx
shl edx,16
or edx,eax
add edx,0x10000 ;+1MB
.mem_get_ok:
mov [total_mem_bytes], edx
.error_hlt:
mov byte [gs:480],'['
mov byte [gs:482],'4'
mov byte [gs:484],']'
mov byte [gs:486],' '
mov al,[total_mem_bytes+3]
add al,'0'
mov byte [gs:488],'0'
mov byte [gs:490],al
mov al,[total_mem_bytes+2]
add al,'0'
mov byte [gs:492],'0'
mov byte [gs:494],al
mov al,[total_mem_bytes+1]
add al,'0'
mov byte [gs:496],'0'
mov byte [gs:498],al
mov al,[total_mem_bytes+0]
add al,'0'
mov byte [gs:500],'0'
mov byte [gs:502],al
ret
def_print_loader:
mov bx,160
mov cx,loader_str_len
mov si,0
print_loader_loop:
mov al, [loader_str + si]
mov [gs:bx],al
inc bx
mov al, [loader_str_color]
mov [gs:bx],al
inc bx
add si,1
loop print_loader_loop
ret
loader_str db '[2] This is loader code'
loader_str_len equ $-loader_str
loader_str_color db 0000_0111b
def_open_A20:
in al,0x92
or al,0x02
out 0x92,al
ret
def_loader_GDT_set_PE:
lgdt[gdt_ptr]
mov eax,cr0
or eax,0x01
mov cr0,eax
ret
; =================== loader_start =======================
loader_start:
call def_print_loader
call def_read_card_num
call def_open_A20
call def_loader_GDT_set_PE
jmp dword SELECTOR_CODE:p_mode_start
LOADER_STACK_TOP equ LOADER_BASE_ADDR
[bits 32]
p_mode_start:
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
mov ax,SELECTOR_VIDEO
mov gs,ax
mov eax,KERNEL_START_SECTOR
mov ebx,KERNEL_BIN_BASE_ADDR
mov ecx,200
call def_rd_disk_m_32
call def_print_into32
call def_setup_page
jmp SELECTOR_CODE:enter_kernel
enter_kernel:
call kernel_init
mov esp,0xc009f000
call def_6print_kernel
jmp KERNEL_ENTRY_POINT
;jmp $
; ======================= [bits 32 code] =======================
def_6print_kernel:
mov ebx,800
mov ecx,kernel_str_len
mov di,0
.kernel_loop:
mov al,[kernel_str+di]
mov byte [gs:ebx],al
add ebx,2
inc di
loop .kernel_loop
ret
kernel_str db '[6] This is kernel'
kernel_str_len equ $-kernel_str
def_print_into32:
mov ebx,320
mov ecx,into32_len
mov di,0
.into32_loop:
mov al,[into32_str+di]
mov byte [gs:ebx],al
add ebx,2
inc di
loop .into32_loop
ret
into32_str db '[3] This is into32'
into32_len equ $-into32_str
def_setup_page:
mov ecx,4096 ;1024*4个页目录项
mov esi,0
.clear_page_dir:
mov byte [PAGE_DIR_TABLE_POS + esi],0
inc esi
loop .clear_page_dir
.create_pde:
mov eax,PAGE_DIR_TABLE_POS
add eax,0x1000
mov ebx,eax
or eax,PG_US_U | PG_RW_W | PG_P
mov [PAGE_DIR_TABLE_POS +0x0],eax;第一个页表放入第一个页目录项
mov [PAGE_DIR_TABLE_POS + 0xc00],eax;第一个页表放入最高1G的页目录项
sub eax,0x1000;得到页目录首地址
mov [PAGE_DIR_TABLE_POS + 4092], eax;将页目录首地址放到最后一个目录项中
;创建页表项,前256个
mov ecx,256
mov esi,0
mov edx,PG_US_U | PG_RW_W | PG_P
.create_pte:
mov [ebx+esi*4],edx
add edx,4096
inc esi
loop .create_pte
;创建高1GB中的其他页目录项
mov eax,PAGE_DIR_TABLE_POS
add eax,0x2000
or eax, PG_US_U | PG_RW_W | PG_P
mov ebx, PAGE_DIR_TABLE_POS
mov ecx,254
mov esi,769 ;769是1G中的第一个目录项
.create_kernel_pde:
mov [ebx+esi*4],eax
inc esi
add eax,0x1000
loop .create_kernel_pde
;设置配置
sgdt[gdt_ptr]
mov ebx,[gdt_ptr + 2]; ebx = GDT_BASE
or dword [ebx + 0x18 + 4], 0xc0000000; GDT_BASE + 3*8 + 4,到了VIDEO的高4字节
add dword[gdt_ptr + 2], 0xc0000000;为高系统区1GB映射做准备
add esp, 0xc0000000;将栈指针映射到内核
mov eax,PAGE_DIR_TABLE_POS
mov cr3, eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax;开启cr0 31位
lgdt [gdt_ptr]; 重新加载GDT
call def_print_va
ret
def_print_va:
mov ebx,640
mov ecx,va_len
mov di,0
.virtual_addr_loop:
mov al,[virtual_addr+di]
mov byte [gs:ebx],al
add ebx,2
inc di
loop .virtual_addr_loop
ret
virtual_addr db '[5] virtual addr'
va_len equ $-virtual_addr
;----------- 将kernel.bin 中的segment拷贝到编译的地址 ----------
kernel_init:
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
mov dx, [KERNEL_BIN_BASE_ADDR + 42]
mov ebx,[KERNEL_BIN_BASE_ADDR + 28]
add ebx, KERNEL_BIN_BASE_ADDR
mov cx, [KERNEL_BIN_BASE_ADDR + 44]
.each_segment:
cmp byte [ebx + 0], PT_NULL
je .PTNULL
push dword [ebx + 16]
mov eax,[ebx + 4]
add eax,KERNEL_BIN_BASE_ADDR
push eax
push dword [ebx + 8]
call mem_cpy
add esp,12
.PTNULL:
add ebx,edx
loop .each_segment
ret
;--------------------- mem_cpy ------------------------|
;-------- 逐字节拷贝 void mem_cpy(dst, src, size) -----|
;------------------------------------------------------|
mem_cpy:
cld
push ebp
mov ebp,esp
push ecx
mov edi, [ebp + 8]
mov esi, [ebp + 12]
mov ecx, [ebp + 16]
rep movsb
pop ecx
pop ebp
ret
;------------------------ rd_disk_m_32 ----------------
def_rd_disk_m_32:
mov esi,eax
mov di,cx
;指定扇区 0x1f2 8bit
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
;LBA 0-7 8bit
mov dx,0x1f3
out dx,al
;LBA 8-15 8bit
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA地址23-16位写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
;第3步:向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;至此,硬盘控制器便从指定的 lba 地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就把 cx 个扇区的数据读出来。
;第4步,检查硬盘状态
.not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz .not_ready
;第5步,从0x1f0端口读数据
mov ax,di
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [ebx],ax
add ebx,2
loop .go_on_read
ret
结果图: