参照《ORANGE’S:一个操作系统的实现》
汇编源码如下:boot.asm
- org 07c00h ; where the code will be running
- mov ax, cs
- mov ds, ax
- mov es, ax
- call DispStr ; let's display a string
- jmp $ ; and loop forever
- DispStr:
- mov ax, BootMessage
- mov bp, ax ; ES:BP = string address
- mov cx, 16 ; CX = string length
- mov ax, 01301h ; AH = 13, AL = 01h
- mov bx, 000ch ; RED/BLACK
- mov dl, 0
- int 10h
- ret
- BootMessage: db "Hello, OS world!"
- times 510-($-$$) db 0 ; fill zeros to make it exactly 512 bytes
- dw 0xaa55 ; boot record signature
批处理命令行如下,生成boot.bin
- nasm boot.asm -f bin -o boot.bin
- pause
boot.bin文件二进制内容如下:
在qemu下执行命令行如下:
c:\qemu>qemu-system-i386.exe boot.bin
执行结果如下,显示了书中一致的Hello,OS world!
参照《ORANGE’S:一个操作系统的实现》
在实模式下,16位的寄存器需要用“段:偏移”这种方法才能达到1MB的寻址能力,如今我们有了32位寄存器,一个寄存器就 可以寻址4GB的空间,是不是从此段值就被抛弃了呢?实际上并没有,新政策下的地址仍然用“段:偏移”这样的形式来表示,只不 过保护模式下“段”的概念发生了根本性的变化。实模式下,段值还是可以看做是地址的一部分的,段值为XXXXh表示以XXXX0h开 始的一段内存。而保护模式下,虽然段值仍然由原来16位的cs、ds等寄存器表示,但此时它仅仅变成了一个索引,这个索引指向一 个数据结构的一个表项,表项中详细定义了段的起始地址、界限、属性等内容。这个数据结构,就是GDT(实际上还可能是LDT,这 个以后再介绍)。GDT中的表项也有一个专门的名字,叫做描述符(Descriptor)。
参照《ORANGE’S:一个操作系统的实现》
现在我们假设已经有了一个内核,Loader肯定要加载它入内存,而且内核开始执行的时候肯定已经在保护模式下了,所 以,Loader要做的事情至少有两件:
在实模式下把内核加载加载进入内存;
跳入保护模式。
然后在保护模式下运行内核。
参照《ORANGE’S:一个操作系统的实现》
完成从实模式到保护模式跳转这一任务的应该是Loader,那么Loader应该走多远呢?只完成跳转,还是应该把 GDT、IDT、8259A等内容准备完备?实际上,从逻辑上讲,Loader不是操作系统的一部分,所以不应该越俎代庖。而且,你一定也 希望早早结束Loader的工作进入正题,所以,我们还是要让Loader尽量简单,其余的工作留给内核来做。
参照《ORANGE’S:一个操作系统的实现》
我猜你一定已经发现了其中的诀窍,不外乎就是关键字global和extern。是的,有了这两个关 键字,就可以方便地在汇编和C代码之间自由来去。
- touch boot.S
- nano boot.S
输入如下内容
利用nasm进行编译
nasm -o boot boot.S
创建虚拟磁盘映像
bximage -mode=create -hd=60 -q os.raw
填充第一扇区
dd if=boot of=os.raw bs=512 count=1
采用qemu运行
qemu-system-i386 os.raw
如下所示