【手写操作系统实战】01 - 安装Cygwin + bochs 环境
1. CYGWIN 下载和配置
下载setup-x86_64.exe,选择从网络安装, 全部默认安装,默认会安装在C盘下面。
软件包安装时,先安装gcc-core,gcc-g++,nasm 这几个,后面还需要其他的时候,重新运行setup-x86_64.exe就可以了, 它既是安装程序也是软件包安装程序,重新安装时,已有的文件不会丢失。
运行成功如下:
2. 编写一个简单的汇编程序
在 cygwin下创建一个目录code,然后写一个简单的汇编程序 boot.asm , 放在 C:\cygwin64\home\Administrator\code 这个目录下就可以了
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 Files\Bochs-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 字节。
|