我正在嘗試在 GAS 程式集中制作我自己的引導程式。到目前為止,我可以使用 BIOS 中斷列印到螢屏上。我試圖將磁盤讀入記憶體,但模擬器中的輸出是:
Booting... (PANIC) Disk error Press any key to reboot..._
這是我的代碼:
.code16
.text
.org 0x0
.global main
main:
jmp start # jump to beginning of code
nop
bpb:
iOEM: .ascii "Canary" # OEM String
iSectSize: .word 0x200 # bytes per sector
iClustSize: .byte 1 # sectors per cluster
iResSect: .word 1 # #of reserved sectors
iFatCnt: .byte 2 # #of FAT copies
iRootSize: .word 224 # size of root directory
iTotalSect: .word 2880 # total # of sectors if over 32 MB
iMedia: .byte 0xf0 # media Descriptor
iFatSize: .word 9 # size of each FAT
iTrackSect: .word 9 # sectors per track
iHeadCnt: .word 2 # number of read-write heads
iHiddenSect: .int 0 # number of hidden sectors
iSect32: .int 0 # # sectors for over 32 MB
iBootDrive: .byte 0 # holds drive that the boot sector came from
iReserved: .byte 0 # reserved, empty
iBootSign: .byte 0x29 # extended boot sector signature
iVolID: .ascii "seri" # disk serial
acVolumeLabel: .ascii "VOLUME A" # volume label
acFSType: .ascii "FAT16" # file system type
.func print
print:
lodsb # load byte from si into al, increment si
cmp $0, %al # test if character is 0 (end)
je print_done # jump to end if 0.
mov $0x0e, %ah # set teletype output
mov $9, %bx # set bh (page no.) to 0, and bl (attribute) to white (9)
int $0x10 # int 10h
jmp print # repeat for next character.
print_done:
ret
.endfunc
.func reboot
reboot:
mov $rebootmsg, %si # load address of reboot message into si
call print # print the string
mov $0x00, %ah
mov $0x00, %al
int $0x16 # wait for a key press
.byte 0xea # machine language to jump to ffff:0000 (reboot)
.word 0x0000
.word 0xffff
.endfunc
.func readSector
readSector:
mov $0x00, %cx # counter = 0
read:
push %ax # store logical block in stack
push %cx # store counter in stack
push %bx # store data buffer offset in stack
# Cylinder = (LBA / SectorsPerTrack) / NumHeads
# Sector = (LBA mod SectorsPerTrack) 1
# Head = (LBA / SectorsPerTrack) mod NumHeads
mov iTrackSect, %bx # get sectors per track
mov $0x00, %dx
# Divide (dx:ax/bx to ax,dx)
# Quotient (ax) = LBA / SectorsPerTrack
# Remainder (dx) = LBA mod SectorsPerTrack
div %bx
inc %dx # increment remainder since it is a sector
mov %dl, %cl # store result in cl to use for int 13h
mov iHeadCnt, %bx # get number of heads
mov $0x00, %dx
# Divide (dx:ax/bx to ax,dx)
# Quotient (ax) = Cylinder
# Remainder (dx) = Head
div %bx
mov %al, %ch # ch = cylinder
mov %dl, %dh # dh = head
mov $0x02, %ah # subfunction 2
mov $0x01, %al # no. of sectors to read
mov iBootDrive, %dl # drive number
pop %bx # restore data buffer offset
int $0x13
jc readFailure # retry if carry flag is set (error)
pop %cx
pop %ax
ret
# On error, retry 4 times before jumping to bootFailure
readFailure:
pop %cx # get counter from stack
inc %cx
cmp $4, %cx # check if we completed 4 tries
je bootFailure # jump to bootFailure if even after 4 tries we get an error
# Reset disk system
mov $0x00, %ah
mov $0x00, %al
int $0x13
# Retry
pop %ax
jmp read
.endfunc
start:
# Setup segments:
cli
mov %dl, iBootDrive # save what drive we booted from (should be 0x0)
mov %cs, %ax # cs = 0x0, since that's where boot sector is (0x07c00)
mov %ax, %ds # cs = cs = 0x0
mov %ax, %es # cs = cs = 0x0
mov %ax, %ss # cs = cs = 0x0
mov $0x7c00, %sp # Stack grows down from offset 0x7c00 toward 0x0000.
sti
# Clear the screen
mov $0x00, %ah
mov $0x03, %al # Set video mode (80x25 text mode, 16 colors)
int $0x10
# Reset disk system
# Jump to bootFailure on error
mov iBootDrive, %dl # drive to reset
mov $0x00, %ah
mov $0x00, %al
int $0x13
jc bootFailure # display error message if carry set (error)
# Display message if successful
mov $msg, %si
call print
call readSector
# Reboot
call reboot
bootFailure:
mov $diskerror, %si
call print
call reboot
# Program Data
msg: .asciz "Booting...\r\n"
diskerror: .asciz "(PANIC) Disk error\r\n"
rebootmsg: .asciz "Press any key to reboot..."
.fill (510-(.-main)), 1, 0 # Pad with nulls up to 510 bytes (excl. boot magic)
.word 0xaa55 # magic word for BIOS
我究竟做錯了什么?另外,如果有更好更有效的方法來撰寫此代碼,請告訴。
uj5u.com熱心網友回復:
您的bpb位置錯誤且長度錯誤!
jmp start # jump to beginning of code nop bpb: iOEM: .ascii "Canary" # OEM String ... acVolumeLabel: .ascii "VOLUME A" # volume label acFSType: .ascii "FAT16" # file system type
因為開始標簽與這個初始標簽相距超過 127 個位元組jmp,匯編器將使用 3 個位元組對跳轉進行編碼。但是額外的nop將使得bpb從偏移 4 開始,在法律上它必須從偏移 3 開始。要么放棄,nop要么讓起點更近。
BS_OEMName欄位的長度必須為 8 個位元組。你的“金絲雀”太短了。
BS_VolLab欄位的長度必須為 11 個位元組。你的“VOLUME A”太短了。
BS_FilSysType欄位的長度必須為 8 個位元組。你的“FAT16”太短了。
設定段暫存器是錯誤的。
start: # Setup segments: cli mov %dl, iBootDrive # save what drive we booted from (should be 0x0) mov %cs, %ax # cs = 0x0, since that's where boot sector is (0x07c00) mov %ax, %ds # cs = cs = 0x0 mov %ax, %es # cs = cs = 0x0 mov %ax, %ss # cs = cs = 0x0 mov $0x7c00, %sp # Stack grows down from offset 0x7c00 toward 0x0000. sti
無法保證%cs引導扇區程式啟動時代碼段暫存器為 0,但您將其內容復制到其他段暫存器。此外,.org 0x0要加載到段暫存器中的正確值必須是 0x07C0,而不是零!
當然,您應該在%ds正確加載之后再保存引導驅動器代碼。
我的建議是:
.org 0x7C00
...
start:
cli
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $0x7C00, %sp
mov %dl, iBootDrive
sti
為什么它必須從磁盤讀取失敗
call print call readSector
You don't specify the logical block that you want to read from!
The print routine certainly uses %ax leaving a non-sensical value in it, and you never setup the desired LBA in %ax before calling readSector. That's never going to work. Additionally you also forget to specify the data buffer offset in %bx.
Once you'll have amended all of the above, you could try to load the second sector of the disk (CHS = 0,0,2) so as to verify that reading is fine:
mov $0x00, %dh # dh = head
mov iBootDrive, %dl # drive number
mov $0x0002, %cx # ch = cylinder, cl = sector
mov $0x7E00, %bx # data buffer offset
mov $0x0201, %ax # ah = subfunction, al = no. of sectors to read
int $0x13
jc readFailure
Good luck!
Also, if there is a better more efficient way to write this code, please tell.
我沒有過多地驗證readSector例程中的代碼。我有點假設它無論如何都復制了。重點是,如果您對優化代碼感興趣,我建議您在更正后,在代碼審查論壇上發布它的作業版本。
uj5u.com熱心網友回復:
在 x86 匯編中讀取磁盤時如何處理錯誤?
一般來說,用于處理錯誤:
a) 嘗試解決它(例如,如果它是“壞扇區”錯誤,則可能切換到相同資料的不同冗余副本),并嘗試設計作業系統,以便可以解決常見問題。對于啟動代碼(用戶無法使用作業系統訪問作業系統幫助或互聯網,因為作業系統無法啟動)這比普通代碼更重要。
b) 嘗試使用漂亮/易于理解的語言(沒有神秘的廢話或錯誤代碼)向用戶(如果有用戶)報告錯誤,以增加用戶弄清楚如何解決問題的機會(例如如果硬體有問題,他們知道他們的硬體有問題并且可以更換正確的硬體)。對于啟動代碼(用戶無法使用作業系統訪問作業系統幫助或互聯網,因為作業系統無法啟動)這比普通代碼更重要。
c) 嘗試提供用戶可以包含在旨在幫助開發人員/您解決問題的錯誤報告中的詳細資訊。這在撰寫代碼時也很重要,因為它使您更容易修復發現的錯誤。
d) 嘗試以某種方式記錄錯誤。這對于間歇性問題(例如“在寒冷的早晨失敗,在其他時間作業”可能是一個非常重要的線索)和無人值守的系統(例如管理員可能遠程登錄并檢查日志的遠程服務器)很重要。
到目前為止,我可以使用 BIOS 中斷列印到螢屏上。我試圖將磁盤讀入記憶體,但模擬器中的輸出是:
" (PANIC) Disk error" 錯誤訊息本質上是無用的 - 它無法幫助任何人確定這是用戶的錯誤(例如在錯誤的時間拔出/彈出磁盤),或者是否存在硬體故障而不是錯誤,或者它是否是軟體錯誤。想象一下,一個沮喪的 IT 人員給您打電話說“作業系統無法啟動。我們公司每天因生產力下降而損失 123456.78 美元。如果明天之前還沒有修復,我們將起訴您。錯誤訊息是Disk error。 ” .
如果你看一下可能的錯誤代碼的串列(例如,在http://www.ctyme.com/intr/rb-0606.htm),其int 0x13, ah=0x02可以回傳AH; 您會注意到其中一些錯誤代碼可以輕松確定可能是用戶的錯誤(例如“磁盤更改”錯誤)或硬體故障(例如“檢測到壞扇區”)或軟體錯誤(例如“無效”) AH 中的函式或無效引數"); 所有這些都為問題所在提供了非常有用的線索。
請注意,(真正的)軟盤是出了名的不可靠(發生間歇性讀取錯誤的可能性很高);并且(如果磁盤最近沒有使用過)可能會報告錯誤,因為電機的旋轉速度還不夠快。由于這些原因,在軟盤上,建議軟體重試 3 次以上(以嘗試解決問題),然后再假設錯誤會持續存在;這是您可以重置磁盤系統的地方(例如,可能每隔一秒重試一次)。還; 不要忘記 BIOS 在啟動引導加載程式之前立即從磁盤成功加載了引導扇區,因此沒有理由在引導加載程式開始時重置磁盤系統(您已經有證據表明磁盤系統正在作業)適當地)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/343799.html
上一篇:NASM中的細分市場?
下一篇:是否可以讓多個鍵指向同一個物件?
