3.html

来自「linux 0.11中文版 有注释」· HTML 代码 · 共 351 行 · 第 1/2 页

HTML
351
字号
<a name='L166'>cmp ax,#0x9000 ! 已经把从0x8000 段开始的64k 代码移动完?
<a name='L167'>jz end_move
<a name='L168'>mov ds,ax ! source segment ! ds:si??源地址(初始为0x1000:0x0)
<a name='L169'>sub di,di
<a name='L170'>sub si,si
<a name='L171'>mov cx,#0x8000 ! 移动0x8000 字(64k 字节)。
<a name='L172'>rep
<a name='L173'>movsw
<a name='L174'>jmp do_move
<a name='L175'>
<a name='L176'>! then we load the segment descriptors
<a name='L177'>! 此后,我们加载段描述符。
<a name='L178'>! 从这里开始会遇到32 位保护模式的操作,因此需要Intel 32 位保护模式编程方面的知识了,
<a name='L179'>! 有关这方面的信息请查阅列表后的简单介绍或附录中的详细说明。这里仅作概要说明。
<a name='L180'>!
<a name='L181'>! lidt 指令用于加载中断描述符表(idt)寄存器,它的操作数是6 个字节,0-1 字节是描述符表的
<a name='L182'>! 长度值(字节);2-5 字节是描述符表的32 位线性基地址(首地址),其形式参见下面
<a name='L183'>! 219-220 行和223-224 行的说明。中断描述符表中的每一个表项(8 字节)指出发生中断时
<a name='L184'>! 需要调用的代码的信息,与中断向量有些相似,但要包含更多的信息。
<a name='L185'>!
<a name='L186'>! lgdt 指令用于加载全局描述符表(gdt)寄存器,其操作数格式与lidt 指令的相同。全局描述符
<a name='L187'>! 表中的每个描述符项(8 字节)描述了保护模式下数据和代码段(块)的信息。其中包括段的
<a name='L188'>! 最大长度限制(16 位)、段的线性基址(32 位)、段的特权级、段是否在内存、读写许可以及
<a name='L189'>! 其它一些保护模式运行的标志。参见后面205-216 行。
<a name='L190'>!
<a name='L191'>
<a name='L192'>end_move:
<a name='L193'>mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
<a name='L194'>mov ds,ax ! ds 指向本程序(setup)段。
<a name='L195'>lidt idt_48 ! load idt with 0,0
<a name='L196'>! 加载中断描述符表(idt)寄存器,idt_48 是6 字节操作数的位置
<a name='L197'>! (见218 行)。前2 字节表示idt 表的限长,后4 字节表示idt 表
<a name='L198'>! 所处的基地址。
<a name='L199'>lgdt gdt_48 ! load gdt with whatever appropriate
<a name='L200'>! 加载全局描述符表(gdt)寄存器,gdt_48 是6 字节操作数的位置
<a name='L201'>! (见222 行)。
<a name='L202'>
<a name='L203'>! that was painless, now we enable A20
<a name='L204'>! 以上的操作很简单,现在我们开启A20 地址线。参见程序列表后有关A20 信号线的说明。
<a name='L205'>
<a name='L206'>call empty_8042 ! 等待输入缓冲器空。
<a name='L207'>! 只有当输入缓冲器为空时才可以对其进行写命令。
<a name='L208'>mov al,#0xD1 ! command write ! 0xD1 命令码-表示要写数据到
<a name='L209'>out #0x64,al ! 8042 的P2 端口。P2 端口的位1 用于A20 线的选通。
<a name='L210'>! 数据要写到0x60 口。
<a name='L211'>call empty_8042 ! 等待输入缓冲器空,看命令是否被接受。
<a name='L212'>mov al,#0xDF ! A20 on ! 选通A20 地址线的参数。
<a name='L213'>out #0x60,al
<a name='L214'>call empty_8042 ! 输入缓冲器为空,则表示A20 线已经选通。
<a name='L215'>
<a name='L216'>! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
<a name='L217'>! we put them right after the intel-reserved hardware interrupts, at
<a name='L218'>! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
<a name='L219'>! messed this up with the original PC, and they haven't been able to
<a name='L220'>! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
<a name='L221'>! which is used for the internal hardware interrupts as well. We just
<a name='L222'>! have to reprogram the 8259's, and it isn't fun.
<a name='L223'>!! 希望以上一切正常。现在我们必须重新对中断进行编程??
<a name='L224'>!! 我们将它们放在正好处于intel 保留的硬件中断后面,在int 0x20-0x2F。
<a name='L225'>!! 在那里它们不会引起冲突。不幸的是IBM 在原PC 机中搞糟了,以后也没有纠正过来。
<a name='L226'>!! PC 机的bios 将中断放在了0x08-0x0f,这些中断也被用于内部硬件中断。
<a name='L227'>!! 所以我们就必须重新对8259 中断控制器进行编程,这一点都没劲。
<a name='L228'>
<a name='L229'>mov al,#0x11 ! initialization sequence
<a name='L230'>! 0x11 表示初始化命令开始,是ICW1 命令字,表示边
<a name='L231'>! 沿触发、多片8259 级连、最后要发送ICW4 命令字。
<a name='L232'>out #0x20,al ! send it to 8259A-1 ! 发送到8259A 主芯片。
<a name='L233'>.word 0x00eb,0x00eb ! jmp $+2, jmp $+2 ! $ 表示当前指令的地址,
<a name='L234'>! 两条跳转指令,跳到下一条指令,起延时作用。
<a name='L235'>out #0xA0,al ! and to 8259A-2 ! 再发送到8259A 从芯片。
<a name='L236'>.word 0x00eb,0x00eb
<a name='L237'>mov al,#0x20 ! start of hardware int's (0x20)
<a name='L238'>out #0x21,al ! 送主芯片ICW2 命令字,起始中断号,要送奇地址。
<a name='L239'>.word 0x00eb,0x00eb
<a name='L240'>mov al,#0x28 ! start of hardware int's 2 (0x28)
<a name='L241'>out #0xA1,al ! 送从芯片ICW2 命令字,从芯片的起始中断号。
<a name='L242'>.word 0x00eb,0x00eb
<a name='L243'>mov al,#0x04 ! 8259-1 is master
<a name='L244'>out #0x21,al ! 送主芯片ICW3 命令字,主芯片的IR2 连从芯片INT。
<a name='L245'>.word 0x00eb,0x00eb !参见代码列表后的说明。
<a name='L246'>mov al,#0x02 ! 8259-2 is slave
<a name='L247'>out #0xA1,al ! 送从芯片ICW3 命令字,表示从芯片的INT 连到主芯
<a name='L248'>! 片的IR2 引脚上。
<a name='L249'>.word 0x00eb,0x00eb
<a name='L250'>mov al,#0x01 ! 8086 mode for both
<a name='L251'>out #0x21,al ! 送主芯片ICW4 命令字。8086 模式;普通EOI 方式,
<a name='L252'>! 需发送指令来复位。初始化结束,芯片就绪。
<a name='L253'>.word 0x00eb,0x00eb
<a name='L254'>out #0xA1,al !送从芯片ICW4 命令字,内容同上。
<a name='L255'>.word 0x00eb,0x00eb
<a name='L256'>mov al,#0xFF ! mask off all interrupts for now
<a name='L257'>out #0x21,al ! 屏蔽主芯片所有中断请求。
<a name='L258'>.word 0x00eb,0x00eb
<a name='L259'>out #0xA1,al !屏蔽从芯片所有中断请求。
<a name='L260'>
<a name='L261'>! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
<a name='L262'>! need no steenking BIOS anyway (except for the initial loading :-).
<a name='L263'>! The BIOS-routine wants lots of unnecessary data, and it's less
<a name='L264'>! "interesting" anyway. This is how REAL programmers do it.
<a name='L265'>!
<a name='L266'>! Well, now's the time to actually move into protected mode. To make
<a name='L267'>! things as simple as possible, we do no register set-up or anything,
<a name='L268'>! we let the gnu-compiled 32-bit programs do that. We just jump to
<a name='L269'>! absolute address 0x00000, in 32-bit protected mode.
<a name='L270'>!! 哼,上面这段当然没劲??,希望这样能工作,而且我们也不再需要乏味的BIOS 了(除了
<a name='L271'>!! 初始的加载?。BIOS 子程序要求很多不必要的数据,而且它一点都没趣。那是“真正”的
<a name='L272'>!! 程序员所做的事。
<a name='L273'>
<a name='L274'>! 这里设置进入32 位保护模式运行。首先加载机器状态字(lmsw - Load Machine Status Word),也称
<a name='L275'>! 控制寄存器CR0,其比特位0 置1 将导致CPU 工作在保护模式。
<a name='L276'>mov ax,#0x0001 ! protected mode (PE) bit ! 保护模式比特位(PE)。
<a name='L277'>lmsw ax ! This is it! ! 就这样加载机器状态字!
<a name='L278'>jmpi 0,8 ! jmp offset 0 of segment 8 (cs) ! 跳转至cs 段8,偏移0 处。
<a name='L279'>! 我们已经将system 模块移动到0x00000 开始的地方,所以这里的偏移地址是0。这里的段
<a name='L280'>! 值的8 已经是保护模式下的段选择符了,用于选择描述符表和描述符表项以及所要求的特权级。
<a name='L281'>! 段选择符长度为16 位(2 字节);位0-1 表示请求的特权级0-3,linux 操作系统只
<a name='L282'>! 用到两级:0 级(系统级)和3 级(用户级);位2 用于选择全局描述符表(0)还是局部描
<a name='L283'>! 述符表(1);位3-15 是描述符表项的索引,指出选择第几项描述符。所以段选择符
<a name='L284'>! 8(0b0000,0000,0000,1000)表示请求特权级0、使用全局描述符表中的第1 项,该项指出
<a name='L285'>! 代码的基地址是0(参见209 行),因此这里的跳转指令就会去执行system 中的代码。
<a name='L286'>
<a name='L287'>! This routine checks that the keyboard command queue is empty
<a name='L288'>! No timeout is used - if this hangs there is something wrong with
<a name='L289'>! the machine, and we probably couldn't proceed anyway.
<a name='L290'>! 下面这个子程序检查键盘命令队列是否为空。这里不使用超时方法 - 如果这里死机,
<a name='L291'>! 则说明PC 机有问题,我们就没有办法再处理下去了。
<a name='L292'>! 只有当输入缓冲器为空时(状态寄存器位2 = 0)才可以对其进行写命令。
<a name='L293'>empty_8042:
<a name='L294'>.word 0x00eb,0x00eb ! 这是两个跳转指令的机器码(跳转到下一句),相当于延时空操作。
<a name='L295'>in al,#0x64 ! 8042 status port ! 读AT 键盘控制器状态寄存器。
<a name='L296'>test al,#2 ! is input buffer full? ! 测试位2,输入缓冲器满?
<a name='L297'>jnz empty_8042 ! yes - loop
<a name='L298'>ret
<a name='L299'>
<a name='L300'>gdt: ! 全局描述符表开始处。描述符表由多个8 字节长的描述符项组成。
<a name='L301'>! 这里给出了3 个描述符项。第1 项无用(206 行),但须存在。第2 项是系统代码段
<a name='L302'>! 描述符(208-211 行),第3 项是系统数据段描述符(213-216 行)。每个描述符的具体
<a name='L303'>! 含义参见列表后说明。
<a name='L304'>.word 0,0,0,0 ! dummy ! 第1 个描述符,不用。
<a name='L305'>! 这里在gdt 表中的偏移量为0x08,当加载代码段寄存器(段选择符)时,使用的是这个偏移值。
<a name='L306'>.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
<a name='L307'>.word 0x0000 ! base address=0
<a name='L308'>.word 0x9A00 ! code read/exec
<a name='L309'>.word 0x00C0 ! granularity=4096, 386
<a name='L310'>! 这里在gdt 表中的偏移量是0x10,当加载数据段寄存器(如ds 等)时,使用的是这个偏移值。
<a name='L311'>.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
<a name='L312'>.word 0x0000 ! base address=0
<a name='L313'>.word 0x9200 ! data read/write
<a name='L314'>.word 0x00C0 ! granularity=4096, 386
<a name='L315'>
<a name='L316'>idt_48:
<a name='L317'>.word 0 ! idt limit=0
<a name='L318'>.word 0,0 ! idt base=0L
<a name='L319'>
<a name='L320'>gdt_48:
<a name='L321'>.word 0x800 ! gdt limit=2048, 256 GDT entries
<a name='L322'>! 全局表长度为2k 字节,因为每8 字节组成一个段描述符项
<a name='L323'>! 所以表中共可有256 项。
<a name='L324'>.word 512+gdt,0x9 ! gdt base = 0X9xxxx
<a name='L325'>! 4 个字节构成的内存线性地址:0x0009&lt;&lt;16 + 0x0200+gdt
<a name='L326'>! 也即0x90200 + gdt(即在本程序段中的偏移地址,205 行)。
<a name='L327'>
<a name='L328'>.text
<a name='L329'>endtext:
<a name='L330'>.data
<a name='L331'>enddata:
<a name='L332'>.bss
<a name='L333'>endbss:
</pre>
<hr>
<a name='BOTTOM'>
<i><font color='green'>/* [&lt;][&gt;][^][v]<a href='#TOP'>[top]</a>[bottom]<a href='../mains.html'>[index]</a><a href='../help.html'>[help]</a> */</font></i>
</body>
</html>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?