1.准备环境
后面的很多工具都只能在linux上运行,所以需要一台运行linux的PC或者虚拟机。
- nasm:用于将汇编文件(asm)编译成二进制文件(bin)
nasm -f bin xxx.asm -o xxx.bin
- qemu:用于将二进制文件运行在一个虚拟机中
qemu-system-x86-64 xxx.bin
1.引导扇区
该部分主要是为了:创建一个文件 ,让BIOS 将文件识别为一个可引导的磁盘
计算机上电启动后,首先运行主板 BIOS 程序,BIOS 把加载操作系统的任务交给引导扇区。 因此,引导扇区必须放在一个已知的标准位置,这个位置就是磁盘的第一个扇区(cylinder 0, head 0, sector 0) 该扇区一共占用 512 字节。
为了确保 “磁盘是可引导的” BIOS 会检查扇区的第 511 和 512 字节是不是 0xaa55.
最简单的引导扇区如下
e9 fd ff 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 29 more lines with sixteen zero-bytes each ]
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa
用汇编代码实现如下
; 循环
loop:
jmp loop
; 用于填充引导扇区剩余的空间,使引导扇区大小为512字节
times 510-($-$$) db 0
; 写入0xaa55让BOIS识别为可引导的磁盘
dw 0xaa55
运行结果如下
2.引导打印
通过中断int
可以让引导程序输出一些文本
mov ah, 0x0e ; tty mode
mov al, 'H'
int 0x10 ;视频中断,将al的数据输出到屏幕上
mov al, 'e'
int 0x10
mov al, 'l'
int 0x10
int 0x10 ; 'l' is still on al, remember?
mov al, 'o'
int 0x10
jmp $ ; jump to current address = infinite loop
; padding and magic number
times 510 - ($-$$) db 0
dw 0xaa55
3.引导内存
BIOS 会把引导程序放置在内存地址 0x7C00 处,然后把系统控制权交给这里的程序。所以对于用地址去取值首先需要进行地址偏移。
比如下面这段程序,将the_secret
地址处的值输出到屏幕上需要在前面加个全局地址偏移[org 0x7c00]
[org 0x7c00]
mov al, [the_secret]
int 0x10
the_secret:
db "X"
4.引导栈
bp
存储栈的基地址,sp
存储栈顶地址,栈从bp
开始向下生长
mov bp, 0x8000 ; 找一个远离0x7c00的地址作为栈的基地址
mov sp, bp ; 栈为空
push 'A'
push 'B'
push 'C'
pop bx ;将栈中的数据弹到bl中
pop bx
pop bx
5.引导段
段寄存器:代码段cs
,数据段ds
,栈ss
,其他es
。
有了段寄存器后要计算绝对地址就需要与段地址相加。
6.引导扇区
[org 0x7c00]
mov bp, 0x8000 ; 设置栈
mov sp, bp
mov bx, 0x9000 ; es:bx = 0x0000:0x9000 = 0x09000
mov dh, 2 ; read 2 sectors
;调用磁盘读取函数
call disk_load
mov dx, [0x9000] ; retrieve the first loaded word, 0xdada
call print_hex
call print_nl
mov dx, [0x9000 + 512] ; first word from second loaded sector, 0xface
call print_hex
jmp $
%include "../05-bootsector-functions-strings/boot_sect_print.asm"
%include "../05-bootsector-functions-strings/boot_sect_print_hex.asm"
%include "boot_sect_disk.asm"
; Magic number
times 510 - ($-$$) db 0
dw 0xaa55
; boot sector = sector 1 of cyl 0 of head 0 of hdd 0
; from now on = sector 2 ...
times 256 dw 0xdada ; sector 2 = 512 bytes
times 256 dw 0xface ; sector 3 = 512 bytes