Linux-0.11源代码阅读二 实模式到保护模式

bootsect部分已经执行完成,程序也跳转到setup部分:

start:

! ok, the read went well so we get current cursor position and save it for
! posterity.

	mov	ax,#INITSEG	! this is done in bootsect already, but...
	mov	ds,ax
	mov	ah,#0x03	! read cursor pos
	xor	bh,bh
	int	0x10		! save it in known place, con_init fetches
	mov	[0],dx		! it from 0x90000.

首先读取光标的位置,然后存储在内存0x90000地址处,从前面来看,bootsect部分代码存储在内存地址0x90000处,但是现在已经没有什么用了,使用了这段内存来保存一些参数(并没有开辟新的内存空间,重复利用该段内存空间)。


! Get memory size (extended mem, kB)

	mov	ah,#0x88
	int	0x15
	mov	[2],ax

然后调用int 15h中断例程获取内存大小,单位kB。


! Get video-card data:

	mov	ah,#0x0f
	int	0x10
	mov	[4],bx		! bh = display page
	mov	[6],ax		! al = video mode, ah = window width

读取显卡数据。


! check for EGA/VGA and some config parameters

	mov	ah,#0x12
	mov	bl,#0x10
	int	0x10
	mov	[8],ax
	mov	[10],bx
	mov	[12],cx

检查显示方式和获取配置参数。


! Get hd0 data

	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x41]
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0080
	mov	cx,#0x10
	rep
	movsb

将第一个硬盘参数表复制到内存0x90080地址处,大小16个字节,存储在0x90080地址处。


! Get hd1 data

	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x46]
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	rep
	movsb

将第二个硬盘参数表复制到内存0x90090地址处,大小16个字节,存储在0x90090地址处。


! Check that there IS a hd1 :-)

	mov	ax,#0x01500
	mov	dl,#0x81
	int	0x13
	jc	no_disk1
	cmp	ah,#3
	je	is_disk1
no_disk1:
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	mov	ax,#0x00
	rep
	stosb
is_disk1:

检查是否存在第二个硬盘(hd1),如果不存在,则将0x90090地址处的硬盘参数表清零。


! now we want to move to protected mode ...

	cli			! no interrupts allowed !

即将进入保护模式下工作,首先是关闭中断。


! first we move the system to it‘s rightful place

	mov	ax,#0x0000
	cld			! ‘direction‘=0, movs moves forward
do_move:
	mov	es,ax		! destination segment
	add	ax,#0x1000
	cmp	ax,#0x9000
	jz	end_move
	mov	ds,ax		! source segment
	sub	di,di
	sub	si,si
	mov 	cx,#0x8000
	rep
	movsw
	jmp	do_move

然后是将system部分从内存0x10000地址处移动到内存0x00000处,移动大小是0x80000,即512kB大小(即system部分不能超过512kB)。


! then we load the segment descriptors

end_move:
	mov	ax,#SETUPSEG	! right, forgot this at first. didn‘t work :-)
	mov	ds,ax
	lidt	idt_48		! load idt with 0,0
	lgdt	gdt_48		! load gdt with whatever appropriate

用于加载全局描述符(GDT)表和中断描述符表(IDT)。


! that was painless, now we enable A20

        call    empty_8042
	mov     al,#0xD1                ! command write
	out     #0x64,al
	call    empty_8042
	mov     al,#0xDF                ! A20 on
	out     #0x60,al
	call    empty_8042

empty_8042子函数用于测试8042控制器输入缓冲区是否为空,只有为空的情况下才能对其进行写命令操作。

! This routine checks that the keyboard command queue is empty
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn‘t proceed anyway.
empty_8042:
        .word   0x00eb,0x00eb
	in      al,#0x64        ! 8042 status port
	test    al,#2           ! is input buffer full?
	jnz     empty_8042      ! yes - loop
	ret

那么前面用于使能A20地址总线,寻址空间也就从1MB变成4GB。


! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won‘t mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven‘t been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259‘s, and it isn‘t fun.

        mov     al,#0x11                ! initialization sequence
        out     #0x20,al                ! send it to 8259A-1
        .word   0x00eb,0x00eb           ! jmp $+2, jmp $+2
        out     #0xA0,al                ! and to 8259A-2
        .word   0x00eb,0x00eb
        mov     al,#0x20                ! start of hardware int‘s (0x20)
        out     #0x21,al
        .word   0x00eb,0x00eb
        mov     al,#0x28                ! start of hardware int‘s 2 (0x28)
        out     #0xA1,al
        .word   0x00eb,0x00eb
        mov     al,#0x04                ! 8259-1 is master
        out     #0x21,al
        .word   0x00eb,0x00eb
        mov     al,#0x02                ! 8259-2 is slave
        out     #0xA1,al
        .word   0x00eb,0x00eb
        mov     al,#0x01                ! 8086 mode for both
        out     #0x21,al
        .word   0x00eb,0x00eb
        out     #0xA1,al
        .word   0x00eb,0x00eb
        mov     al,#0xFF                ! mask off all interrupts for now
        out     #0x21,al
        .word   0x00eb,0x00eb
        out     #0xA1,al

上面这段代码用于初始化8259A中断控制器。


! well, that certainly wasn‘t fun :-(. Hopefully it works, and we don‘t
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it‘s less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now‘s the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.

	mov     ax,#0x0001      ! protected mode (PE) bit
	lmsw    ax              ! This is it!
	jmpi    0,8             ! jmp offset 0 of segment 8 (cs)

最后真正的进入保护模式,跳转到0地址处,即system部分。

Linux-0.11源代码阅读二 实模式到保护模式,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。