出于学习目的,我正在尝试用汇编语言编写引导加载程序和内核。当我汇编代码并用它启动虚拟机时,引导加载程序似乎工作正常,但内核从未启动。我认为我跳转到了错误的说明,但不知道需要进行哪些更正才能解决我的问题。也有可能我试图错误地制作软盘,但我不认为这是问题所在。当我在十六进制编辑器中查看图像文件时,它们似乎已正确附加。另一个原因可能是从软盘读取了错误的扇区。我正在尝试让内核进入引导加载程序之后的扇区并从中读取内核。
为了构建并运行此代码,我在 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 作为软盘驱动器安装,并在 guest 系统上运行它。
结果是
Patrick's Bootloader Started. Floppy has been reset. Reading Kernel Sector Kernel Sector Loaded
。并且内核永远不会启动。
这是我的代码
bootloader_2.asm
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_2.asm
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
最佳答案
冒着指出显而易见的风险,您的脚本没有复制正确的文件。在某些地方您引用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
关于程序集引导加载程序未跳转到内核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25582905/