本帖最后由 TSA 于 2026-1-6 23:57 编辑
环境需求:
- ubuntu16(任意版本都可,作者用的16)
- nasm编译器
- qemu或者bochs
- clion或者其他c语言编程环境
BOOT基础结构:
在编写前我们需要了解BOOT汇编代码的基础结构以及它的一些伪指令:
ORG:ORG用来指定相对位置,后面的值要与程序在内存中的值一致,下图为程序固定分配图:
用法:
[Asm] 纯文本查看 复制代码 [ORG 0x7c00]
section:用于定义段
用法:
[Asm] 纯文本查看 复制代码 [section .text] ;表示代码段
[section .data] ;表示数据段
[section .bss] ;表示栈段
bits : 用于向编译器说明编译位数,有16位和32位
用法:
[Asm] 纯文本查看 复制代码 [BITS 16]
global : 代码是用于定义入口点,主要将函数导出为编译器等可见,类似于extern,数据上用于定义全局变量
用法:
[Asm] 纯文本查看 复制代码 global _start
times : 定义一条指令或者一组指令重复次数
用法:
[Asm] 纯文本查看 复制代码 times 510 - ($ - $$ ) db 0
db 0x55,0xaa
上述代码中$表示整个1代码结尾位置,$$表示开始位置,减去就是整个代码的大小,510减去代码大小在db填充0,填充0x55与0xaa,0x55与0xaa的填充是硬性要求,应为内核会检查这俩位,如果不是就不会载入
equ : 定义常量
用法:
[Asm] 纯文本查看 复制代码 MAIN_SETUP equ 0x500
还有字符串定义:
[Asm] 纯文本查看 复制代码 msg:
db "hello.world",10,13,0
10 和 13 ascii码表示回车和换行,0结尾
以上便是汇编编写BOOT的一些基础函数,接下来我们就可以进行正式的编写:
首先固定模式:
[Asm] 纯文本查看 复制代码 [ORG 0x7c00] ;定义入口地址,MBR载入BOOT的入口地址固定为0x7c00
[section .text] ;定义代码段
[Bits 16] ;定义16b编译
global _start ;入口函数定义
其次实现主函数,主函数实现比较简单:
[Asm] 纯文本查看 复制代码 _start:
mov ax,3 ;传入参数3给屏幕中断
int 0x10 ;屏幕中断调用,实现清屏
mov si,msg ;将msg字符串地址传给si寄存器
call print ;调用定义的打印函数
jmp $ ;跳回开头
msg字符串实现:
[Asm] 纯文本查看 复制代码 msg:
db "hello,world",10,13,0
print函数实现,在print中打印字符,是通过int 0x10 屏幕中断实现,又int 0x10单次只能打印一个字符,所以需要通过循环进而打印整个字符串:
[Asm] 纯文本查看 复制代码 print:
mov ah,0x0e ; 设置int 0x10功能号 0x0e
mov bh,0 ;设置显示页号,默认0,即当前页
mov bl,0x01 ;设置前景色,可用可不用
.loop:
mov al,[si] ;将字符传递给al寄存器
cmp al,0 ;比较
jz .done
int 0x10 ;调用中断
inc si
jmp .loop
.done:
ret
综上,我们可以得到完整代码:
[Asm] 纯文本查看 复制代码 [ORG 0x7c00]
[section .text]
[Bits 16]
global _start
_start:
xchg bx,bx
xchg bx,bx
mov ax,3
int 0x10
mov si,msg
call print
jmp $
print:
mov ah,0x0e
mov bh,0
mov bl,0x01
.loop:
mov al,[si]
cmp al,0
jz .done
int 0x10
inc si
jmp .loop
.done:
ret
msg:
db "hello,world",10,13,0
times 510 - ($ - $$ ) db 0
db 0x55,0xaa
我们要将其运行,首先进行编译,将其编译为二进制文件:
[Asm] 纯文本查看 复制代码 nasm boot.asm
创建硬盘文件:
[Asm] 纯文本查看 复制代码 bximage -q -hd=16 -func=create -sectsize=512 -imgmode=flat hd.img
-q 表示不进行交互-hd 表示创建硬盘大小为16M-func 功能是创建-section 扇区大小-imgmode 存储格式 flat表示扁平二进制,没有元数据,头部,压缩hd.img 表示输出名字
将编译的二进制文件copy到创建的硬盘文件:
[Asm] 纯文本查看 复制代码 dd if=boot of=hd.img bs=512 seek=0 count=1 conv=notrunc
dd 拷贝工具if input file 输入文件of output file 输出文件bs 块大小为512seek 表示从第几块扇区开始写count 写几块conv=notrunc 表示写完后不截断,且只覆盖前512
qemu运行:
[Asm] 纯文本查看 复制代码 qemu-system-x86_64 -hda hd.img
可以看到我们成功的通过BOOT,打印字符串 |