讀取磁盤:LBA方式


LBA簡介

磁盤讀取發展

IO操作讀取硬盤的三種方式:

  • chs方式 :小於8G (8064MB)

  • LBA28方式:小於137GB

  • LBA48方式:小於144,000,000 GB

LBA方式訪問使用了data寄存器,LBA寄存器(總共3個),device寄存器,command寄存器來完成的。

LBA28和LBA48方式:
LBA28方式使用28位來描述一個扇區地址,最大支持128GB的硬磁盤容量。

LBA28的寄存器

寄存器 端口 作用
data寄存器 0x1F0 已經讀取或寫入的數據,大小為兩個字節(16位數據)
每次讀取1個word,反復循環,直到讀完所有數據
features寄存器 0x1F1 讀取時的錯誤信息
寫入時的額外參數
sector count寄存器 0x1F2 指定讀取或寫入的扇區數
LBA low寄存器 0x1F3 lba地址的低8位
LBA mid寄存器 0x1F4 lba地址的中8位
LBA high寄存器 0x1F5 lba地址的高8位
device寄存器 0x1F6 lba地址的前4位(占用device寄存器的低4位)
主盤值為0(占用device寄存器的第5位)
第6位值為1
LBA模式為1,CHS模式為0(占用device寄存器的第7位)
第8位值為1
command寄存器 0x1F7 讀取,寫入的命令,返回磁盤狀態
1 讀取扇區:0x20 寫入扇區:0x30
磁盤識別:0xEC

IDE通道1,讀寫0x1f0-0x1f7號端口

IDE通道2,讀寫0x170-0x17f號端口

CHS方式:

寫0x1f1: 0

寫0x1f2: 要讀的扇區數

寫0x1f3: 扇區號W

寫0x1f4: 柱面的低8位

寫0x1f5: 柱面的高8位

寫0x1f6: 75位,101,第4位0表示主盤,1表示從盤,30位,磁頭號

寫0x1f7: 0x20為讀, 0x30為寫

讀0x1f7: 第4位為0表示讀寫完成,否則要一直循環等待

讀0x1f0: 每次讀取1個word,反復循環,直到讀完所有數據

24-bit LBA方式:

寫0x1f1: 0

寫0x1f2: 要讀的扇區數

寫0x1f3: LBA參數的0~7位

寫0x1f4: LBA參數的8~15位

寫0x1f5: LBA參數的16~23位

寫0x1f6: 75位,111,第4位0表示主盤,1表示從盤,30位,LBA參數的24~27位

寫0x1f7: 0x20為讀, 0x30為寫

讀0x1f7: 第4位為0表示讀寫完成,否則要一直循環等待

讀0x1f0: 每次讀取1個word,反復循環,直到讀完所有數據

48-bit LBA方式:

寫兩次0x1f1端口: 0

寫兩次0x1f2端口: 第一次要讀的扇區數的高8位,第二次低8位

寫0x1f3: LBA參數的24~31位

寫0x1f3: LBA參數的0~7位

寫0x1f4: LBA參數的32~39位

寫0x1f4: LBA參數的8~15位

寫0x1f5: LBA參數的40~47位

寫0x1f5: LBA參數的16~23位

寫0x1f6: 75位,010,第4位0表示主盤,1表示從盤,30位,0

寫0x1f7: 0x24為讀, 0x34為寫

LBA和CHS的的對應關系

雖然LBA和CHS的兩種定位方式不同,但其實兩者間還是有一個轉換關系的。

讀取硬盤

1)sector count寄存器寄存器寫入讀取的扇區數
2)LBA low寄存器,LBA mid寄存器,LBA high寄存器寫入lba地址
3)device寄存器寫入lba地址和讀取模式
4)command寄存器寫入寫入命令
5)讀取兩個字節數據,多次循環直到讀取完扇區數據。

代碼

boot.asm
引導文件,初始化屏幕后,讀取硬盤並加載4個扇區到內存位置[0x90000]處。然后跳轉到0x90000處執行指令。

;Rats OS
;Tab=4
[bits 16]

    org     0x7c00 				;指明程序的偏移的基地址

;----------- loader const ------------------
LOADER_SECTOR_LBA  		equ 0x1		;第2個邏輯扇區開始
LOADER_SECTOR_COUNT		equ 9		;讀取9個扇區
LOADER_BASE_ADDR 		equ 0x9000  ;內存地址0x9000
;-------------------------------------------

;引導扇區代碼 
    jmp     Entry
    db      0x90
    db      "RATSBOOT"     		;啟動區的名稱可以是任意的字符串(8字節)    

;程序核心內容
Entry:

    ;------------------
    ;初始化寄存器
    mov ax,0				
    mov ss,ax
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov gs,ax
    mov sp,0x7c00

    ;------------------
    ;清屏
    mov ah,0x06				;清除屏幕					
    mov al,0
    mov cx,0   
    mov dx,0xffff  
    mov bh,0x17				;屬性為藍底白字
    int 0x10
    

    mov ah,0x02				;光標位置初始化
    mov dx,0
    mov bh,0
    mov dh,0x0
    mov dl,0x0
    int 0x10

    ;------------------
    ;讀取硬盤1-10扇區
    mov ebx,LOADER_SECTOR_LBA 		;LBA扇區號
    mov cx,LOADER_SECTOR_COUNT		;讀取扇區數
    mov di,LOADER_BASE_ADDR			;寫入內存地址
    call Func_ReadLBA16
    
    jmp LOADER_BASE_ADDR

; ------------------------------------------------------------------------
; 讀取磁盤:Func_ReadLBA16
; 參數:
; ebx 扇區邏輯號
; cx 讀入的扇區數,8位
; di 讀取后的寫入內存地址
; ------------------------------------------------------------------------	
Func_ReadLBA16:
    ;設置讀取的扇區數
    mov al,cl
    mov dx,0x1F2
    out dx,al
    
    ;設置lba地址
    ;設置低8位
    mov al,bl
    mov dx,0x1F3
    out dx,al
    
    ;設置中8位
    shr ebx,8
    mov al,bl
    mov dx,0x1F4
    out dx,al
    
    ;設置高8位
    shr ebx,8
    mov al,bl
    mov dx,0x1F5
    out dx,al
    
    ;設置高4位和device
    shr ebx,8
    and bl,0x0F
    or bl,0xE0
    mov al,bl
    mov dx,0x1F6
    out dx,al
        
    ;設置commond
    mov al,0x20
    mov dx,0x1F7
    out dx,al

.check_status:;檢查磁盤狀態
    nop
    in al,dx
    and al,0x88			;第4位為1表示硬盤准備好數據傳輸,第7位為1表示硬盤忙
    cmp al,0x08
    jnz .check_status   ;磁盤數據沒准備好,繼續循環檢查
    

        
    ;設置循環次數到cx
    mov ax,cx 			;乘法ax存放目標操作數
    mov dx,256
    mul dx
    mov cx,ax			;循環次數 = 扇區數 x 512 / 2 
    mov bx,di
    mov dx,0x1F0
    
.read_data: 				
    in ax,dx			;讀取數據
    mov [bx],ax			;復制數據到內存
    add bx,2    		;讀取完成,內存地址后移2個字節
    
    loop .read_data
    ret


FillSector:
    resb    510-($-$$)       	;處理當前行$至結束(1FE)的填充
    db      0x55, 0xaa

loader.asm
被引導扇區加載到0x90000位置,執行輸出hello in loader文字

;Rats OS
;Tab=4
[bits 16]

section loader vstart=LOADER_BASE_ADDR ;指明程序的偏移的基地址

;----------- loader const ------------------
LOADER_BASE_ADDR 		equ 0x9000  ;內存地址0x9000
;---------------------------------------	
	jmp Entry
	
;程序核心內容
Entry:
	

	;---------------------------
    ;輸出字符串
    mov si,HelloMsg			;將HelloMsg的地址放入si
    mov dh,0				;設置顯示行
	mov dl,0				;設置顯示列
    call Func_Sprint			;調用函數

	
	jmp $			;讓CPU掛起,等待指令


		
; ------------------------------------------------------------------------
; 顯示字符串函數:Func_Sprint
; 參數:
; si = 字符串開始地址,
; dh = 第N行,0開始
; dl = 第N列,0開始
; ------------------------------------------------------------------------
Func_Sprint:
            mov cx,0			;BIOS中斷參數:顯示字符串長度
            mov bx,si
    .len:;獲取字符串長度
            mov al,[bx]			;讀取1個字節到al
            inc bx				;讀取下個字節
            cmp al,0			;是否以0結束
            je .sprint
            inc	cx				;計數器
            jmp .len
    .sprint:;顯示字符串
            mov bx,si
            mov bp,bx
            mov bx,ds
            mov es,bx			;BIOS中斷參數:計算[ES:BP]為顯示字符串開始地址

            mov ah,0x13			;BIOS中斷參數:中斷模式
            mov al,0x01			;BIOS中斷參數:輸出方式
            mov bh,0x0			;BIOS中斷參數:指定分頁為0
            mov bl,0x1F			;BIOS中斷參數:顯示屬性,指定白色文字			
            int 0x10			;調用BIOS中斷操作顯卡。輸出字符串
            ret
; ------------------------------------------------------------------------
;准備顯示字符串
HelloMsg: db "hello in loader!",0
	times	512-($-$$) db  0 ; 處理當前行$至結束(1FE)的填充	

運行

創建Makefile文件,並執行make命令

# tools
PLATFORM=Linux
NASM=nasm
QEMU=qemu-system-x86_64
QEMU-IMG=qemu-img
BOCHS=bochs
BX-IMG=bximage

# args
boot=boot
build=build

target: prepare img  
	$(BOCHS) -f bochsrc.me


img: $(build)/ratsos.img	
	@echo "build img completed"

$(build)/ratsos.img:$(build)/boot.bin $(build)/loader.bin 
	$(BX-IMG) -hd -mode=flat -size=32 -q $(build)/ratsos.img
	sleep 1
	dd if=$(build)/boot.bin of=$(build)/ratsos.img bs=512 count=1  conv=notrunc
	dd if=$(build)/loader.bin of=$(build)/ratsos.img bs=512 count=1 seek=1 conv=notrunc

$(build)/%.bin: $(boot)/%.asm
	$(NASM) -f bin -o $(build)/$*.bin $(boot)/$*.asm	

prepare: $(build)
	@echo "prepare dir $(build)"
    ifeq ($(build), $(wildcard $(build)))
		@echo "build directory exist..."
    else
		mkdir -p $(build)
    endif

clean:
	@echo "clean dir $(build)"
	rm -rf $(build)/*

platform:
	@echo $(PLATFORM)

運行結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM