https://blog.csdn.net/KLKFL/article/details/80730131
https://www.cnblogs.com/joey-hua/p/5528228.html
参考的两篇博客
x86系统在刚开机时CPU处于实模式
计算机在刚打开电源时 :CS=0xFFFF,IP=0x0000
即寻址为0xFFFF0(ROM BIOS映射区)
然后将 0磁道0扇区 的512个字节读入0x7c00处
然后设置 CS=0x07c0,IP=0x0000
此时0x7c00出存放的代码就是从磁盘的引导扇区读入的那512个字节bootsect
此时一些宏的定义:
BOOTSEG = 0x07c0
INITSEG = 0x9000
SETUPSEG = 0x9020
SYSSEG = 0x1000
表示bootsect先被加载到了0x07c0段,然后bootsect的512个字节会被移动到0x9000段,紧跟着的setup段就从0x9020段开始,system段从0x1000开始
bootsect做的事就是把setup加载到内存上,然后显示logo
1 entry _start !程序的入口是_start 2 3 _start: 4 !下面是调用0x10中断,显示光标操作 5 mov ah,#0x03 6 xor bh,bh 7 int 0x10 8 9 mov cx,#36 !字符串msg的长度 10 mov bx,#0x0007 !调用0x10中断时,bx寄存器=0x0007表示显示树形 11 mov bp,#msg1 12 mov ax,#0x07c0 13 mov es,ax !es:bp表示要显示的字符串的地址,当前段(bootsect)就是0x07c0,字符串的地址已经赋值给bp 14 mov ax,#0x1301 15 int 0x10 !调用中断显示logo 16 17 !设置一个死循环保证不退出 18 inf_loop: 19 jmp inf_loop 20 21 22 !这就是要显示的logo 23 msg1: 24 .byte 13,10 25 .ascii "Hello OS world, my name is LZJ" 26 .byte 13,10,13,10 27 28 29 !boot_flag是设置引导扇区的标记,必须由这个才能进行引导,且必须放在最后两个字节 30 !bootsect的大小是512个字节,所以boot_flag得放在第510个字节处 31 .org 510 32 boot_flag: 33 .word 0xAA55
然后用bootsect读取setup
先把setup.s编写好
1 entry _start 2 _start: 3 mov ah,#0x03 4 xor bh,bh 5 int 0x10 6 7 mov cx,#25 8 mov bx,#0x0007 9 mov bp,#msg2 10 mov ax,cs 11 mov es,ax !此时已经将setup加载到了0x9020处,所以字符串的断地址直接设置为cs即可 12 mov ax,#0x1301 13 int 0x10 !调用中断 14 15 inf_loop: !死循环防止退出 16 jmp inf_loop 17 msg1: 18 .byte 13,10 19 .ascii "Hello OS world, my name is LZJ" 20 .byte 13,10,13,10 21 22 .org 510 !这里还是和bootsect一样 23 boot_flag: 24 .word 0xAA55
然后对上面的bootsect.s进行修改让bootsect能够读入setup
1 SETUPLEN=2 !自定义的setup段的长度为2个扇区 2 SETUPSEG=0x07e0 !因为自定仪的代码没有将bootsect移到0x9000出,所以setup段的开始地址在磁盘的的0x07e0处 3 entry _start 4 _start: 5 mov ah,#0x03 6 xor bh,bh 7 int 0x10 8 9 mov cx,#36 10 mov bx,#0x0007 11 mov bp,#msg1 12 mov ax,#0x07c0 13 mov es,ax 14 mov ax,#0x1301 15 int 0x10 16 17 load_setup: !从这里开始进行载入setup 18 19 !0x13号中断是BIOS读磁盘扇区的中断 20 mov dx,#0x0000 !dh=磁头号,dl=驱动器号 21 mov cx,#0x0002 !ch=柱面号,cl=开始扇区 22 mov bx,#0x0200 !表示将读取的内容放在内存es:bp处,es是0x7c0,bp是0x200(前200字节) 23 mov ax,#0x0200+SETUPLEN !ah=0x02表示读磁盘,al=扇区数量,本来应该是4,但是自己写的setup只占用了2个扇区(512字节) 24 int 0x13 !调用中断读取setup 25 26 jnc ok_load_setup !读入成功则跳转 27 28 mov dx,#0x0000 !读入失败则调用0x13中断进行复位重启,再次进行读入 29 mov ax,#0x0000 30 int 0x13 31 jmp load_setup !再次进行读写 32 33 ok_load_setup: 34 jmpi 0,SETUPSEG !cs:ip跳转到0x07e0:0x0000处,即跳转到setup执行 35 36 37 msg1: 38 .byte 13,10 39 .ascii "Hello OS world, my name is LZJ" 40 .byte 13,10,13,10 41 42 !自己写的setup.s只占用了512个字节(两个扇区),实际上linux0.11用了4个扇区 43 .org 510 44 boot_flag: 45 .word 0xAA55
然后用makefile进行编译,可能要修改下build.c
make BootImage
实际上,bootsect在读完setup后还会读入system,其地址为0x10000-0x8ffff
然后是setup需要完成的工作:
完成os启动前的设置,使系统进入保护模式:
首先读入的就是硬件信息:光标,扩展内存数,显卡参数,跟设备号
然后将system模块向下移动到0地址
但是,0地址处本来有重要内容,比如int中断,如何避免冲突?
此时setup让硬件进入保护模式,即int和cs:ip的寻址电路被改变,igt(中断描述符表:用来查中断)和gdt(全局描述符表:用来查cs值对应的段的地址)就是改变后的寻址方式
cs在保护模式中也称为选择子,表示段的编号
具体的实现:
在system移动结束(end_move)后,新建一张临时的gdt表,其内容设置为
下面是gdt表的格式
有一个寄存器叫做cr0,其末尾PE=1为启动保护模式,
所以先把cr0的末尾置为1来进入保护模式,然后执行指令
jmpi 0,8 !cs=0x0008,ip=0x0000
cs=8,对应的gdt从第8个字节开始后的内容恰好为0,所以要跳转的地址为0x0000:0x0000
即system的开头head.s文件
到此为止setup也完成了任务,系统开始执行head.s
参考了一张图片,表示的是在每个阶段每个块的位置
模仿linux0.11的代码
1 mov ax,#INITSEG 2 ! 设置 ds = 0x9000 3 mov ds,ax 4 mov ah,#0x03 5 ! 读入光标位置 6 xor bh,bh 7 ! 调用 0x10 中断 8 int 0x10 9 ! 将光标位置写入 0x90000. 10 mov [0],dx 11 12 ! 读入内存大小位置 13 mov ah,#0x88 14 int 0x15 15 mov [2],ax 16 17 ! 从 0x41 处拷贝 16 个字节(磁盘参数表) 18 mov ax,#0x0000 19 mov ds,ax 20 lds si,[4*0x41] !把内存记录磁盘参数的值赋值给si 21 mov ax,#INITSEG 22 mov es,ax !INITSEG赋值给es 23 mov di,#0x0004 ! 24 mov cx,#0x10 ! 25 ! 重复16次 26 rep 27 movsb
最后是setup.s的全代码
1 INITSEG = 0x9000 2 entry _start 3 _start: 4 ! Print "NOW we are in SETUP" 5 mov ah,#0x03 6 xor bh,bh 7 int 0x10 8 mov cx,#25 9 mov bx,#0x0007 10 mov bp,#msg2 11 mov ax,cs 12 mov es,ax 13 mov ax,#0x1301 14 int 0x10 15 16 mov ax,cs 17 mov es,ax 18 ! init ss:sp 19 mov ax,#INITSEG 20 mov ss,ax 21 mov sp,#0xFF00 22 23 ! Get Params 24 mov ax,#INITSEG 25 mov ds,ax 26 mov ah,#0x03 27 xor bh,bh 28 int 0x10 29 mov [0],dx 30 mov ah,#0x88 31 int 0x15 32 mov [2],ax 33 mov ax,#0x0000 34 mov ds,ax 35 lds si,[4*0x41] 36 mov ax,#INITSEG 37 mov es,ax 38 mov di,#0x0004 39 mov cx,#0x10 40 rep 41 movsb 42 43 ! Be Ready to Print 44 mov ax,cs 45 mov es,ax 46 mov ax,#INITSEG 47 mov ds,ax 48 49 ! Cursor Position 50 mov ah,#0x03 51 xor bh,bh 52 int 0x10 53 mov cx,#18 54 mov bx,#0x0007 55 mov bp,#msg_cursor 56 mov ax,#0x1301 57 int 0x10 58 mov dx,[0] 59 call print_hex 60 ! Memory Size 61 mov ah,#0x03 62 xor bh,bh 63 int 0x10 64 mov cx,#14 65 mov bx,#0x0007 66 mov bp,#msg_memory 67 mov ax,#0x1301 68 int 0x10 69 mov dx,[2] 70 call print_hex 71 ! Add KB 72 mov ah,#0x03 73 xor bh,bh 74 int 0x10 75 mov cx,#2 76 mov bx,#0x0007 77 mov bp,#msg_kb 78 mov ax,#0x1301 79 int 0x10 80 ! Cyles 81 mov ah,#0x03 82 xor bh,bh 83 int 0x10 84 mov cx,#7 85 mov bx,#0x0007 86 mov bp,#msg_cyles 87 mov ax,#0x1301 88 int 0x10 89 mov dx,[4] 90 call print_hex 91 ! Heads 92 mov ah,#0x03 93 xor bh,bh 94 int 0x10 95 mov cx,#8 96 mov bx,#0x0007 97 mov bp,#msg_heads 98 mov ax,#0x1301 99 int 0x10 100 mov dx,[6] 101 call print_hex 102 ! Secotrs 103 mov ah,#0x03 104 xor bh,bh 105 int 0x10 106 mov cx,#10 107 mov bx,#0x0007 108 mov bp,#msg_sectors 109 mov ax,#0x1301 110 int 0x10 111 mov dx,[12] 112 call print_hex 113 114 inf_loop: 115 jmp inf_loop 116 117 print_hex: 118 mov cx,#4 119 print_digit: 120 rol dx,#4 121 mov ax,#0xe0f 122 and al,dl 123 add al,#0x30 124 cmp al,#0x3a 125 jl outp 126 add al,#0x07 127 outp: 128 int 0x10 129 loop print_digit 130 ret 131 print_nl: 132 mov ax,#0xe0d ! CR 133 int 0x10 134 mov al,#0xa ! LF 135 int 0x10 136 ret 137 138 msg2: 139 .byte 13,10 140 .ascii "NOW we are in SETUP" 141 .byte 13,10,13,10 142 msg_cursor: 143 .byte 13,10 144 .ascii "Cursor position:" 145 msg_memory: 146 .byte 13,10 147 .ascii "Memory Size:" 148 msg_cyles: 149 .byte 13,10 150 .ascii "Cyls:" 151 msg_heads: 152 .byte 13,10 153 .ascii "Heads:" 154 msg_sectors: 155 .byte 13,10 156 .ascii "Sectors:" 157 msg_kb: 158 .ascii "KB" 159 160 .org 510 161 boot_flag: 162 .word 0xAA55