- 1. CYGWIN 下载和配置
- 2. 编写一个简单的汇编程序
- 3. 安装bochs模拟器运行boot.bin
- 4. boot.asm代码分析
下载setup-x86_64.exe,选择从网络安装,
全部默认安装,默认会安装在C盘下面。
软件包安装时,先安装gcc-core,gcc-g++,nasm 这几个,后面还需要其他的时候,重新运行setup-x86_64.exe就可以了,
它既是安装程序也是软件包安装程序,重新安装时,已有的文件不会丢失。
运行成功如下:
2. 编写一个简单的汇编程序
在 cygwin下创建一个目录code,然后写一个简单的汇编程序 boot.asm,
放在 C:cygwin64homeAdministratorcode 这个目录下就可以了
org 07c00h ; 告诉编译器程序加载到7c00处 mov ax, cs mov ds, ax mov es, ax call DispStr ; 调用显示字符串例程 jmp $ ; 无限循环 DispStr: mov ax, BootMessage mov bp, ax ; ES:BP = 串地址 mov cx, 50 ; CX = 串长度 mov ax, 01301h ; AH = 13, AL = 01h mov bx, 000ch ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮) mov dl, 0 int 10h ; 10h 号中断 ret BootMessage: db "Hello, OS world, this is Ciellee ^_^" times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节 dw 0xaa55 ; 结束标志
编译汇编文件 nasm.exe boot.asm -o boot.bin
3. 安装bochs模拟器运行boot.bin
下载并安装bochs:https://sourceforge.net/projects/bochs/files/bochs/
安装的时候,注意勾选 DLX Linux Demo,这样我们可以复制其中的 bochsrc.bxrc 以它为模板,来编写配置文件。
配置环境变量
安装好后,开始测试运行 boot.bin。
bochs 的根目录在 C:Program FilesBochs-2.7,
我们从里面找BIOS-bochs-latest、VGABIOS-lgpl-latest、x11-pc-us.map 这几个文件到代码目录下
如下图所示:
bochsrc.bxrc 配置文件,内容如下:
# how much memory the emulated machine will have megs: 32 # filename of ROM images romimage: file=BIOS-bochs-latest vgaromimage: file=VGABIOS-lgpl-latest # what disk images will be used floppya: 1_44=boot.bin, status=inserted # choose the boot disk. boot: floppy # disable the mouse, since DLX is text only mouse: enabled=0 # enable key mapping, using US layout as default. keyboard: keymap=x11-pc-us.map
配置好后,双击运行bochsrc.bxrc 文件,结果如下:
可以看到运行成功了: 打印出了
好了,至此,我们简单的环境就搭建好了。
4. boot.asm代码分析
我们知道,当计算机电源被打开时,它首先会进行加电自检(POST),然后寻找启动盘,
如果是选择从软盘启动,计算机就会检查软盘的0面0磁道1扇区,如果发现它以0xAA55结束,则BIOS认为它是一个引导扇区,同时引导扇区中还应该包括一段少于512字节的执行码。
一旦BIOS发现引导扇区,就会将这512字节的内容装载到内存地址0000:7c00 处,然后跳转0000:7c00 处并将PC指针控制权交给这段引导代码。
org 07c00h ; 告诉编译器程序,这段代码加载到0000:7c00处 mov ax, cs ; 将 代码段CS 地址保存在 AX寄存器中 mov ds, ax ; 使数据段与代码段在同一个段 mov es, ax ; 使附加段与代码段在同一个段 call DispStr ; 调用显示字符串例程,调用子程序DispStr,显示字符串信息 jmp $ ; 表示当前地址,实现死循环 DispStr: ; 子程序DispStr 代码 mov ax, BootMessage ; 将字符串的首地址保存在ax寄存器中 mov bp, ax ; ES:BP = 串地址 mov cx, 50 ; CX = 串长度 mov ax, 01301h ; AH = 13: 中断13h号功能,写字符串; AL=01H: 表示写完字符串后,更新光标位置 mov bx, 000ch ; 页号为0(BH = 0)视频缓冲区是分页的, 黑底红字(BL = 0Ch,高亮) mov dh, 18 ; DH=行号 mov dl, 0 ; DL=写串的光标位置,DL=列号 int 10h ; 10h 号中断 ret BootMessage: db "Hello, OS world, this is Ciellee ^_^" times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节,从当前地址开始到偏移量1FDH处都存储:00H dw 0xaa55 ; 磁盘引导记录结束标志
上面代码,我们更新在第19行,第0列写字符串,编译运行结果如下:
汇编语言中,字体颜色表示如下:
bit-7:置1闪烁
bit 6,5,4,背景色,分别对应RGB
bit-3:高亮位,置1高亮
bit-2,1,0位,前景色,分别对应R(红)G(绿)B(蓝)
比如前面的0C,它的二进制是 0000 1100,也就是背景色黑色,高亮,前景色红色。
比如我们要显示背景色绿色,前景色黄色,并且闪烁,就应该配置 1010 1110,也就是 AE,
如下,我显示了两行字,第一行 (AE)闪烁绿底黄字,第二行(1CE)蓝底红字
org 07c00h ; 告诉编译器程序,这段代码加载到0000:7c00处 mov ax, cs ; 将 代码段CS 地址保存在 AX寄存器中 mov ds, ax ; 使数据段与代码段在同一个段 mov es, ax ; 使附加段与代码段在同一个段 call DispStr ; 调用显示字符串例程,调用子程序DispStr,显示字符串信息 call DispStr2 jmp $ ; 表示当前地址,实现死循环 DispStr: ; 子程序DispStr 代码 mov ax, BootMessage ; 将字符串的首地址保存在ax寄存器中 mov bp, ax ; ES:BP = 串地址 mov cx, 36 ; CX = 串长度 mov ax, 01301h ; AH = 13: 中断13h号功能,写字符串; AL=01H: 表示写完字符串后,更新光标位置 mov bx, 00AEh ; 页号为0(BH = 0)视频缓冲区是分页的, 闪烁绿底黄字 mov dh, 18 ; DH=行号 mov dl, 0 ; DL=写串的光标位置,DL=列号 int 10h ; 10h 号中断 ret DispStr2: ; 子程序DispStr 代码 mov ax, BootMessage2 ; 将字符串的首地址保存在ax寄存器中 mov bp, ax ; ES:BP = 串地址 mov cx, 37 ; CX = 串长度 mov ax, 01301h ; AH = 13: 中断13h号功能,写字符串; AL=01H: 表示写完字符串后,更新光标位置 mov bx, 001Ch ; 蓝底红字 mov dh, 20 ; DH=行号 mov dl, 0 ; DL=写串的光标位置,DL=列号 int 10h ; 10h 号中断 ret BootMessage: db "Hello, OS world, this is Ciellee ^_^" BootMessage2: db "Hello, OS world, this is Ciellee2 ^_^" times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节,从当前地址开始到偏移量1FDH处都存储:00H dw 0xaa55 ; 磁盘引导记录结束标志
times 510-($-$$) db 0
这句话中,
$ 表示当前行被汇编后的地址,表示当前的地址。
$$ 表示一个节(section)的开始处被汇编后的地址,此处表示程序被编译后的开始地址,也就是 0x7c00。
$ - $$ 表示从开头到当前地址的相对距离。
times 510-($-$$) db 0 表示将0这个字节重复 510 - ($ - $$)遍,也就是把剩下的空间不停的填充0,知道程序有510字节为止,
这样加上结束标志 0xAA55 占用的2字节,刚好 512 字节。



