我试图在程序集中编写一个bootloader和内核用于学习目的。当我组装代码并使用它启动虚拟机时,引导加载程序似乎正常工作,但内核永远不会启动。我以为我跳错了,但不知道我需要做些什么更正才能解决我的问题。我也有可能尝试不正确地制作软盘,但我不认为这是问题所在。当我在十六进制编辑器中查看图像文件时,似乎它们被正确附加。另一个原因可能是从软盘读取错误的扇区。我试图让内核在引导加载程序之后立即进入扇区。
要构建并运行此代码,我在Windows Vista x64中执行以下操作:
nasm bootloader_2.asm -f bin -o bootloader_2.bin
nasm kernel_2.asm -f bin -o kernel.bin
partcopy bootloader_2.bin bootloader_2.img 0d 511d
partcopy kernel_2.bin kernel_2.img 0d 511d
copy bootloader.img /b + kernel.img POS_2.img
然后我使用Oracle VM Virtual Box将POS_2.img挂载为软盘驱动器,并在来宾系统上运行。
结果
Patrick的Bootloader已启动。 软盘已重置。 读取内核部门 内核部门已加载
。内核永远不会启动。
这是我的代码
bits 16
org 0x7C00
boot: jmp loader
; OEM Parameter block / BIOS Parameter block (wtf is this for?)
times 0Bh-$+boot DB 0
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xF0
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY"
bsFileSystem: DB "FAT12"
; END PARAMETER BLOCK
; ----- Variables -----
started db "Patrick's Bootloader Started...", 0x0D, 0x0A, 0
floppy_reset_done db "Floppy has been reset.", 0x0D, 0x0A, 0
loading_msg db "Reading Kernel Sector", 0x0D, 0x0A, 0
loading_sucess db "Kernel Sector Loaded", 0x0D, 0x0A, 0
done db "Bootloader Done.", 0x0D, 0x0A, 0
; ----- End Variables -----
; ----- Calls -----
reset_floppy:
mov ah, 0
mov dl, 0
int 0x13
jc reset_floppy
mov si, floppy_reset_done
call print_string
ret
read_kernel:
mov si, loading_msg
call print_string
mov si, 0x0
mov ax, 0x1000 ; setting up the address to read into
mov es, ax ; moving the value in to es
xor bx, bx ; clearing bx
mov ah, 0x02 ; floppy function
mov al, 1 ; read 1 sector
mov ch, 1 ; track
mov cl, 2 ; sector to read
mov dh, 0 ; head number
mov dl, 0 ; drive number
int 0x13 ; BIOS Interrupt Call
jc read_kernel
mov si, loading_sucess
call print_string
ret
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
; input is ax, cx is destroyed
print_hex:
mov cx, 4
.next_digit:
push cx
mov cl, 4
rol ax, cl
push ax
and al, 0x0F
add al, '0'
cmp al, '9'
jle .not_a_leter
add al, 'A'-'9'-1
.not_a_leter:
mov ah, 0x0E
int 0x10
pop ax
pop cx
loop .next_digit
ret
; ----- End of Calls -----
; ===== Bootloader Main =====
loader:
mov si, started
call print_string
call reset_floppy
call read_kernel
jmp 0x1000:0x0
mov si, done ; never reached. Intentional for debugging
call print_string ; these lines failure to produce a result tell us that the jmp was attempted
; ===== End of Bootloader Main =====
times 510-($-$$) db 0
dw 0xAA55
kernel:
jmp k_main
welcome_msg db "Welcome to Patrick's Operating System!", 0x0D, 0x0A, 0
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
k_main:
mov si, welcome_msg
call print_string
.k_main_loop:
jmp .k_main_loop
cli
hlt
times 512-($-$$) db 0
答案 0 :(得分:6)
冒着指出明显的风险,你的脚本不会复制正确的文件。在某些地方,您可以引用kernel.bin
和其他kernel_2.bin
。试试这个:
nasm bootloader_2.asm -f bin -o bootloader_2.bin
nasm kernel_2.asm -f bin -o kernel_2.bin
partcopy bootloader_2.bin bootloader_2.img 0d 511d
partcopy kernel_2.bin kernel_2.img 0d 511d
copy bootloader.img /b + kernel_2.img POS_2.img
您可能还会发现使用dd
这样的事情更容易。它可以作为Cygwin的一部分使用,并且具有可以写入文件或物理设备(如真正的软盘)的优势。
此外,您应该考虑使用Makefile
而不是脚本。当您进一步扩展项目时,它会有所帮助。
也就是说,代码存在三个基本问题。首先,要加载的下一个扇区是在柱面0上,而不是柱面1.其次,代码只能安全地ret
到内核 - 你必须明确地跳转到那里。那部分代码将如下所示:
read_kernel:
mov si, loading_msg
call print_string
mov si, 0x0
mov ax, 0x1000 ; setting up the address to read into
mov es, ax ; moving the value in to es
xor bx, bx ; clearing bx
mov ah, 0x02 ; floppy function
mov al, 1 ; read 1 sector
mov ch, 0 ; cylinder
mov cl, 2 ; sector to read
mov dh, 0 ; head number
mov dl, 0 ; drive number
int 0x13 ; BIOS Interrupt Call
jc read_kernel
push es ; either push the address and retf or use far jmp
push bx ;
mov si, loading_sucess
call print_string
; jmp 0x1000:0 ; alternative to push/retf is simple long jump
retf
第三个问题是内核。您还没有将DS
寄存器更改为指向新的偏移量,因此即使您获得该代码,它也不会打印您想要的字符串。将代码更改为以下内容:
k_main:
push cs ; save the cs reg
pop ds ; use as ds also
mov si, welcome_msg
call print_string
.k_main_loop:
jmp .k_main_loop