执行过bootsect.s,加载了所有系统代码之后,开始向32位模式转变,为main函数的调用做准备,同样,附上图往下看
1 INITSEG = 0x9000 ! we move boot here - out of the way 2 SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). 3 SETUPSEG = 0x9020 ! this is the current segment 4 5 .globl begtext, begdata, begbss, endtext, enddata, endbss 6 .text 7 begtext: 8 .data 9 begdata: 10 .bss 11 begbss: 12 .text 13 14 entry start 15 start: 16 17 //保存当前光标位置 18 mov ax,#INITSEG 19 mov ds,ax ;ds设置成INITSEG 20 mov ah,#0x03 ;int 10读光标功能号3 21 xor bh,bh 22 int 0x10 ;调用中断,读取光标位置 23 mov [0],dx ;光标信息存在dx中,并存入0x90000处 24 mov ah,#0x88 ;int 15取扩展内存大小功能号0x88 25 int 0x15 ;调用中断 26 mov [2],ax ;返回从100000开始的扩展内存大小 27 28 //保存显卡当前显示模式 29 mov ah,#0x0f 30 int 0x10 31 mov [4],bx ; bh = display page 32 mov [6],ax ; al = video mode, ah = window width 33 34 //检查显示方式(EGA/VGA),并选取参数 35 mov ah,#0x12 36 mov bl,#0x10 37 int 0x10 38 mov [8],ax 39 mov [10],bx ;显示内存,显示状态 40 mov [12],cx ;显卡特性参数 41 42 //取第0个硬盘信息 43 mov ax,#0x0000 44 mov ds,ax 45 lds si,[4*0x41];取中断向量,41的值,即硬盘0参数表的地址 46 mov ax,#INITSEG 47 mov es,ax 48 mov di,#0x0080 ;传输向量表到达的目的地址:9000:0080 49 mov cx,#0x10 ;取10字节 50 rep 51 movsb ;循环复制 52 53 //取第一个磁盘信息 54 mov ax,#0x0000 55 mov ds,ax 56 lds si,[4*0x46];取中断向量,46的值,即硬盘1参数表的地址 57 mov ax,#INITSEG 58 mov es,ax 59 mov di,#0x0090 ;传输向量表到达的目的地址:9000:0090 60 mov cx,#0x10 ;取10字节 61 rep 62 movsb 63 64 //检查是否存在第二个硬盘 65 mov ax,#0x01500 66 mov dl,#0x81 67 int 0x13 68 jc no_disk1 ;如果cf==1,跳转,没有第二个磁盘 69 cmp ah,#3 ;判断是否有硬盘 70 je is_disk1 71 //没有则删除第二个硬盘表 72 no_disk1: 73 mov ax,#INITSEGjj 74 mov es,ax 75 mov di,#0x0090 76 mov cx,#0x10 77 mov ax,#0x00 78 rep 79 stosb 80 81 is_disk1: 82 83 //开始保护模式方面的工作 84 85 cli ;不允许中断 86 87 //首先我们将系统模块移动到新的目标位置 88 89 mov ax,#0x0000 90 cld ! 'direction'=0, movs moves forward 91 do_move: 92 mov es,ax ;被复制到的目的地址es:di=>0000:0 93 add ax,#0x1000 94 cmp ax,#0x9000 95 jz end_move 96 mov ds,ax ;原地址ds:si=>1000:0 97 sub di,di 98 sub si,si 99 mov cx,#0x8000 ;移动0x8000字节(64k) 100 rep 101 movsw 102 jmp do_move 103 104 //加载段描述符 105 106 end_move: 107 mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-) 108 mov ds,ax ;ds指向setup段 109 lidt idt_48 ;加载中断描述符表寄存器 110 lgdt gdt_48 ;加载全局描述符表寄存器 111 112 //开启A20地址线,准备进入32位寻址模式 113 114 call empty_8042 115 mov al,#0xD1 ! command write 116 out #0x64,al 117 call empty_8042 118 mov al,#0xDF ! A20 on 119 out #0x60,al 120 call empty_8042 121 122 ! well, that went ok, I hope. Now we have to reprogram the interrupts :-( 123 ! we put them right after the intel-reserved hardware interrupts, at 124 ! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really 125 ! messed this up with the original PC, and they haven't been able to 126 ! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, 127 ! which is used for the internal hardware interrupts as well. We just 128 ! have to reprogram the 8259's, and it isn't fun. 129 130 mov al,#0x11 ! initialization sequence 131 out #0x20,al ! send it to 8259A-1 132 .word 0x00eb,0x00eb ! jmp $+2, jmp $+2,$表示当前指令地址 133 out #0xA0,al ! and to 8259A-2 134 .word 0x00eb,0x00eb 135 mov al,#0x20 ! start of hardware int's (0x20) 136 out #0x21,al 137 .word 0x00eb,0x00eb 138 mov al,#0x28 ! start of hardware int's 2 (0x28) 139 out #0xA1,al 140 .word 0x00eb,0x00eb 141 mov al,#0x04 ! 8259-1 is master 142 out #0x21,al 143 .word 0x00eb,0x00eb 144 mov al,#0x02 ! 8259-2 is slave 145 out #0xA1,al 146 .word 0x00eb,0x00eb 147 mov al,#0x01 ! 8086 mode for both 148 out #0x21,al 149 .word 0x00eb,0x00eb 150 out #0xA1,al 151 .word 0x00eb,0x00eb 152 mov al,#0xFF ! mask off all interrupts for now 153 out #0x21,al 154 .word 0x00eb,0x00eb 155 out #0xA1,al 156 157 ! well, that certainly wasn't fun :-(. Hopefully it works, and we don't 158 ! need no steenking BIOS anyway (except for the initial loading :-). 159 ! The BIOS-routine wants lots of unnecessary data, and it's less 160 ! "interesting" anyway. This is how REAL programmers do it. 161 ! 162 ! Well, now's the time to actually move into protected mode. To make 163 ! things as simple as possible, we do no register set-up or anything, 164 ! we let the gnu-compiled 32-bit programs do that. We just jump to 165 ! absolute address 0x00000, in 32-bit protected mode. 166 167 mov ax,#0x0001 ! protected mode (PE) bit 168 lmsw ax ! This is it! 169 jmpi 0,8 ! jmp offset 0 of segment 8 (cs) 170 ; 跳转到8:0位置,这里的8为实模式下的段选择符,目的地址是0x00000000 171 //关于8的解析,这里的8对应二进制的"1000",这里的后两位0表示内核特权级,倒数第三位的0表示gdt 172 //1表示用全局描述符表的第1项,该项指出代码的基地址是0,也就是接下来执行的head.s 173 174 ! This routine checks that the keyboard command queue is empty 175 ! No timeout is used - if this hangs there is something wrong with 176 ! the machine, and we probably couldn't proceed anyway. 177 178 //检查键盘命令队列是否为空,当输入缓冲器为空则可以对其进行写命令 179 empty_8042: 180 .word 0x00eb,0x00eb 181 in al,#0x64 ! 8042 status port 182 test al,#2 ! is input buffer full? 183 jnz empty_8042 ! yes - loop 184 ret 185 //全局描述符表开始处 186 gdt: 187 .word 0,0,0,0 ! dummy//第一描述符,不用 188 189 //这里在gdt表中的偏移量为08,联系我们上面的jmpi 0,8,也就是调用此处的表内容 190 //加载代码段寄存器时,使用这个偏移 191 .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) 192 .word 0x0000 ! base address=0 193 .word 0x9A00 ! code read/exec 194 .word 0x00C0 ! granularity=4096, 386 195 196 //这里在gdt表中的偏移量是10,当加载数据段寄存器时,使用这个偏移 197 .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) 198 .word 0x0000 ! base address=0 199 .word 0x9200 ! data read/write 200 .word 0x00C0 ! granularity=4096, 386 201 202 idt_48: 203 .word 0 ! idt limit=0 204 .word 0,0 ! idt base=0L 205 206 gdt_48: 207 .word 0x800 ! gdt limit=2048, 256 GDT entries 208 .word 512+gdt,0x9 ! gdt base = 0X9xxxx 209 210 .text 211 endtext: 212 .data 213 enddata: 214 .bss 215 endbss: