我一直在研究爱好的引导程序和内核,但是引导程序无论如何都找不到内核。 这是引导程序:
%define BUFFER_SEG 0x2000
%define BUFFER_OFF 0x0000
%define LOAD_SEG 0x1000
%define LOAD_OFF 0x0000
[bits 16]
[org 0x7c00]
jmp short start
nop
;DISK DESCRIPTION(BIOS PARAMETER BLOCK)
OEMLabel db "BOOT "
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224 ; Number of entries in root dir
; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors dw 2880
MediumByte db 0F0h
SectorsPerFat dw 9
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
Sides dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 0x29
VolumeID dd 00000000h
VolumeLabel db "myOS "
FileSystem db "FAT12 "
;BOOTLOADER
start:
xor ax, ax
mov ds, ax
cli
mov ss, ax
mov sp, 0x7c00
cld
clc
sti
mov [drive], dl
load_root:
mov ax, 19
call lba_to_hts
mov ah, 2
mov al, 14
mov si, BUFFER_SEG
mov es, si
mov bx, BUFFER_OFF
int 13h
jc reset
mov si, load_root_str
call print
search_file:
mov di, BUFFER_OFF
mov cx, word [RootDirEntries]
xor ax, ax
.loop_search:
xchg cx, dx
mov si, filename
mov cx, 11
rep cmpsb
je file_found
add ax, 32
mov di, BUFFER_OFF
add di, ax
xchg dx, cx
loop .loop_search
jmp file_not_found
file_found:
mov ax, word [es:di+15]
mov [cluster], ax
mov ax, 1
call lba_to_hts
mov di, BUFFER_OFF
mov bx, di
mov ah, 2
mov al, 9
load_FAT:
mov si, FAT_str
call print
int 13h
jnc load_file
call reset
jnc load_FAT
jmp disk_error
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG
mov es, ax
xor bx, bx
mov ah, 2
mov al, 1
.load_sector:
mov ax, word [cluster]
add ax, 31
call lba_to_hts
mov ax, LOAD_SEG
mov es, ax
mov bx, word [pointer]
pop ax
push ax
;stc
int 13h
jnc next_cluster
call reset
jmp .load_sector
next_cluster:
mov ax, [cluster]
xor dx, dx
mov bx, 3
mul bx
mov bx, 2
div bx
mov si, BUFFER_OFF
add si, ax
mov ax, word [ds:si]
or dx, dx
jz .even
.odd:
shr ax, 4
jmp short finish_load
.even:
and ax, 0FFFh
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
add word [pointer], 512
jmp next_cluster
.jump_to_file:
pop ax
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
;SUBROUTINES
file_not_found:
mov si, not_found_str
call print
jmp reboot
print:
pusha
mov ah, 0x0E
.next:
lodsb
cmp al,0
je .done
int 0x10
jmp .next
.done:
popa
ret
lba_to_hts:
push ax
push bx
mov bx, ax
xor dx, dx
div word [SectorsPerTrack]
add dl, 1
mov cl, dl
mov ax, bx
xor dx, dx
div word [SectorsPerTrack]
xor dx, dx
div word [Sides]
mov dh, dl
mov ch, al
pop ax
pop bx
mov dl, [drive]
ret
reset:
mov ah, 0
int 13h ;reset disk
jc disk_error ;if failed jump to search fail
ret
disk_error:
mov si, disk_error_str
call print
reboot:
mov si, reboot_pmpt
call print
mov ax, 0
int 16h
mov ax, 0
int 19h
;DATA
load_root_str db 'Loading Root',13,10,0
disk_error_str db 'Disk Error!',13,10,0
reboot_pmpt db 'PRESS A KEY TO REBOOT',13,10,0
not_found_str db 'KERNEL NOT FOUND',13,10,0
FAT_str db 'Loading FAT',13,10,0
load_file_str db 'Loading KERNEL',13,10,0
drive dw 0
cluster dw 0
pointer dw 0
filename db 'KERNEL BIN',0
;PADDING AND SIGNATURE
times (510-($-$$)) db 0x00
dw 0AA55h
这里是内核:
[bits 16] ;16-bit binary format
;VECTORS
os_vectors:
jmp os_main
;KERNEL
os_main:
cli ;clear interrupts
mov ss, ax ;set stack segment and pointer
mov sp, 0FFFFh
sti ;restore interrupts
cld ;set RAM direction(for strings)
mov ax, 1000h ;set registers to match kernel location
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, hello ;print welcome
call print_string
hlt
;SUBROUTINES
print_string:
mov ah, 0x0e
.next_char:
lodsb
cmp al,0
je .done_print
int 0x10
jmp .next_char
.done_print:
ret
;DATA
hello db 'Hello',0
;PADDING
times (512-($-$$)) db 0x00
我填充了其余的扇区,因为我听说有些仿真器如果不是一个完整的扇区大小,则无法正确读取它。我使用命令
#! bin/bash
cd image
hdiutil create -fs MS-DOS -sectors 2880 floppy
cd ../system
nasm -f bin boot.asm -o boot.bin
nasm -f bin kernel.asm -o kernel.bin
cd ..
dd conv=notrunc if=system/boot.bin of=image/floppy.dmg
dev=`hdid -nomount image/floppy.dmg`
sudo mkdir tmp-loop
sudo cp system/kernel.bin tmp-loop/
sudo mount -t msdos ${dev} tmp-loop
diskutil umount tmp-loop
hdiutil detach ${dev}
sudo rm -rf tmp-loop
hdiutil convert image/floppy.dmg -format UDTO -o image/image.iso
进行构建,然后在qemu中进行仿真。我正在Macbook Air上执行此操作。 当我在qemu中模拟我的引导程序时,我总是得到告诉我找不到内核的字符串。我不知道为什么,也不知道如何解决。
答案 0 :(得分:2)
在大多数情况下,您的代码具有正确的想法。引导加载程序中的主要问题在load_file
和next_cluster
中。 lba_to_hts
中还有一个错误。您的内核中还有一些错误也需要纠正。
请认真考虑此建议-安装BOCH副本并使用其调试器而不是QEMU。 BOCH非常适合引导加载程序,因为它可以正确处理20位segment:offset寻址。对于任何实模式代码,BOCH都是出色的工具。学习正确使用调试器可以使您了解寄存器中的内容。检查内存,设置断点等。您应该可以凭经验发现本答案中确定的错误。
lba_to_hts
中的错误可以在这里看到:
lba_to_hts:
push ax
push bx
...
pop ax
pop bx
在开始时先按AX,然后按BX,但是您需要按反向的顺序将其弹出。应该是:
push ax
push bx
...
pop bx
pop ax
在next_cluster
中,此行有问题:
mov ax, word [ds:si]
您已经在FAT(FAT12)表中计算了偏移量,可以在该表中找到下一个群集。问题是 DS 没有指向FAT表在内存中的段,而是将其设置为0000h。您不能使用:
mov ax, word [es:si]
因为已将 ES 设置为内核负载段(LOAD_SEG
= 1000h)。您可以选择保存 DS 寄存器(推入堆栈),并用BUFFER_SEG
加载 DS 。然后,您可以使用:
mov ax, word [ds:si]
next_cluster
完成后,您将不得不通过从堆栈中弹出旧值来恢复DS。应当注意,mov ax, word [ds:si]
与mov ax, word [si]
相同。如果内存操作数中的寄存器不包含 BP ,则通过 DS 隐式完成内存访问,否则通过 SS 隐式完成。 / p>
我不推荐这种方法。解决此问题的最简单方法是将BUFFER_OFF
与引导加载程序放在同一段中(段0000h)。从0000h:8000h到0000h:0ffffh有32KiB的可用内存。如果修改代码以将FAT和根目录结构加载到0000h:8000h中,则可以通过 DS 访问引导加载程序数据,FAT结构和根目录条目。加载内核时,可以将 ES 切换为LOAD_SEG
。
此代码中还有另一个问题:
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
add word [pointer], 512
jmp next_cluster
通过将文件与0FF8h进行比较,检查是否已到达文件的最后一个群集。如果小于0FF8h,则将[指针]加512,以前进到要读取的缓冲区中的下一个偏移量。问题是jmp next_cluster
不会返回读取下一个群集! jmp next_cluster
应该是jmp load_sector
在load_file
中,您具有以下代码:
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG
mov es, ax
xor bx, bx
mov ah, 2
mov al, 1
.load_sector:
mov ax, word [cluster]
add ax, 31
call lba_to_hts
mov ax, LOAD_SEG
mov es, ax
mov bx, word [pointer]
pop ax
push ax
;stc
int 13h
jnc next_cluster
call reset
jmp .load_sector
next_cluster:
在.load_sector
标签之前,您就为Int 13h
BIOS调用设置了AX和BX寄存器。不幸的是,您在标签.load_sector:
之后的行中破坏了AX和BX。中间还有一个不寻常的POP / PUSH,没有任何意义。您可以将这段代码更改为:
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG ; ES=load segment for kernel
mov es, ax
load_sector:
mov ax, word [cluster] ; Get cluster number to read
add ax, 33-2 ; Add 31 to cluster since FAT data area
; starts at Logical Block Address (LBA) 33
; and we need to subtract 2 since valid
; cluster numbers start at 2
call lba_to_hts
mov bx, word [pointer] ; BX=Current offset in buffer to read to
mov ax, 201h ; AH=2 is read, AL=1 read 1 sector
;stc
int 13h
jnc next_cluster
call reset
jmp load_sector
next_cluster:
代码的修订版如下:
%define BUFFER_OFF 0x8000
%define BUFFER_SEG 0x0000
%define LOAD_SEG 0x1000
%define LOAD_OFF 0x0000
[bits 16]
[org 0x7c00]
jmp short start
nop
;DISK DESCRIPTION(BIOS PARAMETER BLOCK)
OEMLabel db "BOOT "
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224 ; Number of entries in root dir
; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors dw 2880
MediumByte db 0F0h
SectorsPerFat dw 9
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
Sides dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 0x29
VolumeID dd 00000000h
VolumeLabel db "myOS "
FileSystem db "FAT12 "
;BOOTLOADER
start:
xor ax, ax
mov ds, ax
cli
mov ss, ax
mov sp, 0x7c00
sti
cld
mov [drive], dl
mov si, BUFFER_SEG ; ES=buffer segment. Only has to be set once
mov es, si
mov bx, BUFFER_OFF
load_root:
mov ax, 19 ; Root directory starts at LBA 19
call lba_to_hts
mov ax, (2<<8) | 14 ; Root directory for this media fits in 14 sectors
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 14'
int 13h
jc reset
mov si, load_root_str
call print
search_file:
mov di, BUFFER_OFF
mov cx, word [RootDirEntries]
xor ax, ax
.loop_search:
xchg cx, dx
mov si, filename
mov cx, 11
rep cmpsb
je file_found
add ax, 32
mov di, BUFFER_OFF
add di, ax
xchg dx, cx
loop .loop_search
jmp file_not_found
file_found:
mov ax, word [di+15] ; Buffer and Bootloader now in same segment DS
; Don't need ES:
mov [cluster], ax
mov ax, 1
call lba_to_hts
mov bx, BUFFER_OFF
mov ax, (2<<8) | 9 ; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 9'
load_FAT:
mov si, FAT_str
call print
int 13h
jnc load_file
call reset
jnc load_FAT
jmp disk_error
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG ; ES=load segment for kernel
mov es, ax
load_sector:
mov ax, word [cluster] ; Get cluster number to read
add ax, 33-2 ; Add 31 to cluster since FAT data area
; starts at Logical Block Address (LBA) 33
; and we need to subtract 2 since valid
; cluster numbers start at 2
call lba_to_hts
mov bx, word [pointer] ; BX=Current offset in buffer to read to
mov ax, (2<<8) | 1 ; AH=2 is read, AL=1 read 1 sector
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 1'
int 13h
jnc next_cluster
call reset
jmp load_sector
next_cluster:
mov ax, [cluster]
xor dx, dx
mov bx, 3
mul bx
mov bx, 2
div bx
mov si, BUFFER_OFF
add si, ax
mov ax, word [si]
or dx, dx
jz .even
.odd:
shr ax, 4
jmp short finish_load
.even:
and ax, 0FFFh
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
add word [pointer], 512 ; We haven't reached end of kernel. Add 512 for next read
jmp load_sector ; Go back and load the next sector
.jump_to_file:
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
;SUBROUTINES
file_not_found:
mov si, not_found_str
call print
jmp reboot
print:
pusha
mov ah, 0x0E
.next:
lodsb
cmp al,0
je .done
int 0x10
jmp .next
.done:
popa
ret
lba_to_hts:
push ax
push bx
mov bx, ax
xor dx, dx
div word [SectorsPerTrack]
add dl, 1
mov cl, dl
mov ax, bx
xor dx, dx
div word [SectorsPerTrack]
xor dx, dx
div word [Sides]
mov dh, dl
mov ch, al
pop bx ; Need to POP in reverse order to the pushes!
pop ax
mov dl, [drive]
ret
reset:
mov ah, 0
int 13h ;reset disk
jc disk_error ;if failed jump to search fail
ret
disk_error:
mov si, disk_error_str
call print
reboot:
mov si, reboot_pmpt
call print
mov ax, 0
int 16h
mov ax, 0
int 19h
;DATA
load_root_str db 'Loading Root',13,10,0
disk_error_str db 'Disk Error!',13,10,0
reboot_pmpt db 'PRESS A KEY TO REBOOT',13,10,0
not_found_str db 'KERNEL NOT FOUND',13,10,0
FAT_str db 'Loading FAT',13,10,0
load_file_str db 'Loading KERNEL',13,10,0
drive dw 0
cluster dw 0
pointer dw 0
filename db 'KERNEL BIN',0
;PADDING AND SIGNATURE
times (510-($-$$)) db 0x00
dw 0AA55h
您不正确地设置了段寄存器,并且堆栈应该位于偶数字节边界上。如果将 SP 设置为零,则第一次推送将从SP的数据中减去2,该数据位于段顶部的0000-2 = 0fffe。我只需将 ES = DS = FS = GS = SS 设置为 CS 。其次,当您执行一条HLT
指令时,它只会暂停直到下一个中断,然后才在HLT
之后进入该指令。如果您想HLT
无限期地先关闭CLI
的中断。最好将HLT
置于循环中,以防收到未被CLI
屏蔽的不可屏蔽中断(NMI)。
您的内核可以通过以下方式更改:
[bits 16] ;16-bit binary format
;VECTORS
os_vectors:
jmp os_main
;KERNEL
os_main:
mov ax, cs ;CS is segment where we were loaded
cli ;clear interrupts
mov ss, ax ;set stack segment and pointer
xor sp, sp ;SP=0. First push will wrap SP to 0fffeh
sti ;restore interrupts
cld ;set RAM direction(for strings)
mov ds, ax ;DS=ES=FS=GS=CS
mov es, ax
mov fs, ax
mov gs, ax
mov si, hello ;print welcome
call print_string
cli ;Turn off interrupts so that HLT doesn't continue
;when an interrupt occurs
.hlt_loop:
hlt
jmp .hlt_loop ; Infinite loop to avoid NMI dropping us into the code of
; print_string
;SUBROUTINES
print_string:
mov ah, 0x0e
.next_char:
lodsb
cmp al,0
je .done_print
int 0x10
jmp .next_char
.done_print:
ret
;DATA
hello db 'Hello',0
您的代码中存在许多效率低下的问题,但是我会解决一些较大的问题。尽管您的next_cluster
代码有效,但它使用的寄存器数量超过了所需的数量,并且在内存中的编码时间更长。为了将任何值乘以3,您可以将值乘以2并将原始值添加到该值。公式如下:
valtimes3 =(值* 2)+值
这很重要,因为要将寄存器中的值乘以2,只需使用SHL
指令将这些位左移一位。通过使用SHR
指令将寄存器中的位右移1来完成2分频。 SHR
的优点在于,您从寄存器中移出的位位于进位标志(CF)中。如果设置了CF,则该值为奇数;如果设置为CF,则该数字为偶数。 next_cluster
代码看起来像:
next_cluster:
mov bx, [cluster] ; BX = current cluster number
mov ax, bx ; AX = copy of cluster number
shl bx, 1 ; BX = BX * 2
add bx, ax ; BX = BX + AX (BX now contains BX * 3)
shr bx, 1 ; Divide BX by 2
mov ax, [bx+BUFFER_OFF] ; Get cluster entry from FAT table
jnc .even ; If carry not set by SHR then result was even
.odd:
shr ax, 4 ; If cluster entry is odd then cluster number is AX >> 4
jmp short finish_load
.even:
and ah, 0Fh ; If cluster entry is even then cluster number is AX & 0fffh
; We just need to and AH with 0fh to achieve the same result
finish_load:
您可以通过重新排列标准计算来简化lba_to_hts
。我已经为此写了一个先前的Stackoverflow answer,使用修订后的公式替换掉了它:
lba_to_chs
函数,该函数采用LBA并将其转换为CHS,并且仅适用于众所周知的IBM兼容软盘格式。; Function: lba_to_chs ; Description: Translate Logical block address to CHS (Cylinder, Head, Sector). ; Works **ONLY** for well known IBM PC compatible **floppy disk formats**. ; ; Resources: http://www.ctyme.com/intr/rb-0607.htm ; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion ; https://stackoverflow.com/q/45434899/3857942 ; Sector = (LBA mod SPT) + 1 ; Head = (LBA / SPT) mod HEADS ; Cylinder = (LBA / SPT) / HEADS ; ; Inputs: SI = LBA ; Outputs: DL = Boot Drive Number ; DH = Head ; CH = Cylinder ; CL = Sector ; ; Notes: Output registers match expectation of Int 13h/AH=2 inputs ; lba_to_chs: push ax ; Preserve AX mov ax, si ; Copy 16-bit LBA to AX div byte [SectorsPerTrack] ; 16-bit by 8-bit DIV : LBA / SPT mov cl, ah ; CL = S = LBA mod SPT inc cl ; CL = S = (LBA mod SPT) + 1 xor ah, ah ; Upper 8-bit of 16-bit value set to 0 for DIV div byte [NumberOfHeads] ; 16-bit by 8-bit DIV : (LBA / SPT) / HEADS mov ch, al ; CH = C = (LBA / SPT) / HEADS mov dh, ah ; DH = H = (LBA / SPT) mod HEADS mov dl, [boot_device] ; boot device, not necessary to set but convenient pop ax ; Restore scratch register ret
您只需要将函数的名称更改为lba_to_hts
;将NumberOfHeads
更改为Sides
;将boot_drive
更改为drive
;并更改代码,以便通过 AX 而不是 SI 传递LBA。 AX 甚至不需要保留编写现有代码的方式。
当发现需要将另一个群集读入内存时,可以有效地将512添加到[pointer]
上,以移至内存中的下一个位置。问题是您将自己限制为65536字节长的内核。一旦达到128 512字节扇区读入内存,您将超过65536(128 * 512 = 65536)。 [pointer]
将自动换行并从0开始,您将覆盖已经读取的内核部分。您可以通过始终将磁盘读取到偏移量0(BX = 0)并将32添加到 ES 来解决此问题。将1加到段寄存器时,将在内存中前进16个字节(一个段落)。如果要前进512字节,请在 ES 中添加32(32 * 16 = 512)。在您的load_sectors
代码中,您可以更改:
call lba_to_hts
mov bx, word [pointer] ; BX=Current offset in buffer to read to
收件人:
call lba_to_hts
xor bx, bx
在Finish_load
中,您可以更改:
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
add word [pointer], 512 ; We haven't reached end of kernel. Add 512 for next read
jmp load_sector ; Go back and load the next sector
.jump_to_file:
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
收件人:
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
mov ax, es
add ax, 32 ; Increasing segment by 1 advances 16 bytes (paragraph)
; in memory. Adding 32 is same advancing 512 bytes (32*16)
mov es, ax ; Advance ES to point at next 512 byte block to read into
jmp load_sector ; Go back and load the next sector
.jump_to_file:
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
实现这些更改的boot.asm
版本看起来像:
%define BUFFER_OFF 0x8000
%define BUFFER_SEG 0x0000
%define LOAD_SEG 0x1000
%define LOAD_OFF 0x0000
[bits 16]
[org 0x7c00]
jmp short start
nop
;DISK DESCRIPTION(BIOS PARAMETER BLOCK)
OEMLabel db "BOOT "
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224 ; Number of entries in root dir
; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors dw 2880
MediumByte db 0F0h
SectorsPerFat dw 9
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
Sides dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 0x29
VolumeID dd 00000000h
VolumeLabel db "myOS "
FileSystem db "FAT12 "
;BOOTLOADER
start:
xor ax, ax
mov ds, ax
cli
mov ss, ax
mov sp, 0x7c00
sti
cld
mov [drive], dl
mov si, BUFFER_SEG ; ES=buffer segment. Only has to be set once
mov es, si
mov bx, BUFFER_OFF
load_root:
mov ax, 19 ; Root directory starts at LBA 19
call lba_to_hts
mov ax, (2<<8) | 14 ; Root directory for this media fits in 14 sectors
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 14'
int 13h
jc reset
mov si, load_root_str
call print
search_file:
mov di, BUFFER_OFF
mov cx, word [RootDirEntries]
xor ax, ax
.loop_search:
xchg cx, dx
mov si, filename
mov cx, 11
rep cmpsb
je file_found
add ax, 32
mov di, BUFFER_OFF
add di, ax
xchg dx, cx
loop .loop_search
jmp file_not_found
file_found:
mov ax, word [di+15] ; Buffer and Bootloader now in same segment DS
; Don't need ES:
mov [cluster], ax
mov ax, 1
call lba_to_hts
mov bx, BUFFER_OFF
mov ax, (2<<8) | 9 ; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 9'
load_FAT:
mov si, FAT_str
call print
int 13h
jnc load_file
call reset
jnc load_FAT
jmp disk_error
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG ; ES=load segment for kernel
mov es, ax
load_sector:
mov ax, word [cluster] ; Get cluster number to read
add ax, 33-2 ; Add 31 to cluster since FAT data area
; starts at Logical Block Address (LBA) 33
; and we need to subtract 2 since valid
; cluster numbers start at 2
call lba_to_hts
xor bx, bx ; Always read a kernel sector to offset 0
mov ax, (2<<8) | 1 ; AH=2 is read, AL=1 read 1 sector
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 1'
int 13h
jnc next_cluster
call reset
jmp load_sector
next_cluster:
mov bx, [cluster] ; BX = current cluster number
mov ax, bx ; AX = copy of cluster number
shl bx, 1 ; BX = BX * 2
add bx, ax ; BX = BX + AX (BX now contains BX * 3)
shr bx, 1 ; Divide BX by 2
mov ax, [bx+BUFFER_OFF] ; Get cluster entry from FAT table
jnc .even ; If carry not set by SHR then result was even
.odd:
shr ax, 4 ; If cluster entry is odd then cluster number is AX >> 4
jmp short finish_load
.even:
and ah, 0Fh ; If cluster entry is even then cluster number is AX & 0fffh
; We just need to and AH with 0fh to achieve the same result
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
mov ax, es
add ax, 32 ; Increasing segment by 1 advances 16 bytes (paragraph)
; in memory. Adding 32 is same advancing 512 bytes (32*16)
mov es, ax ; Advance ES to point at next 512 byte block to read into
jmp load_sector ; Go back and load the next sector
.jump_to_file:
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
;SUBROUTINES
file_not_found:
mov si, not_found_str
call print
jmp reboot
print:
pusha
mov ah, 0x0E
.next:
lodsb
cmp al,0
je .done
int 0x10
jmp .next
.done:
popa
ret
; Function: lba_to_hts
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
; Works ONLY for well known IBM PC compatible floppy disk formats.
;
; Resources: http://www.ctyme.com/intr/rb-0607.htm
; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
; https://stackoverflow.com/q/45434899/3857942
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: AX = LBA
; Outputs: DL = Boot Drive Number
; DH = Head
; CH = Cylinder
; CL = Sector
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_hts:
div byte [SectorsPerTrack] ; 16-bit by 8-bit DIV : LBA / SPT
mov cl, ah ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor ah, ah ; Upper 8-bit of 16-bit value set to 0 for DIV
div byte [Sides] ; 16-bit by 8-bit DIV : (LBA / SPT) / HEADS
mov ch, al ; CH = C = (LBA / SPT) / HEADS
mov dh, ah ; DH = H = (LBA / SPT) mod HEADS
mov dl, [drive] ; boot device, not necessary to set but convenient
ret
reset:
mov ah, 0
int 13h ;reset disk
jc disk_error ;if failed jump to search fail
ret
disk_error:
mov si, disk_error_str
call print
reboot:
mov si, reboot_pmpt
call print
mov ax, 0
int 16h
mov ax, 0
int 19h
;DATA
load_root_str db 'Loading Root',13,10,0
disk_error_str db 'Disk Error!',13,10,0
reboot_pmpt db 'PRESS A KEY TO REBOOT',13,10,0
not_found_str db 'KERNEL NOT FOUND',13,10,0
FAT_str db 'Loading FAT',13,10,0
load_file_str db 'Loading KERNEL',13,10,0
drive dw 0
cluster dw 0
filename db 'KERNEL BIN',0
;PADDING AND SIGNATURE
times (510-($-$$)) db 0x00
dw 0AA55h
在QEMU中运行时,将显示以下内容: