!
! setup.s (C) 1991 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
;setup.S功能描述
;1.把system模块移至0x0处
;2.加载GDT IDT 打开A20
;3.重设中断号
;4.进入保护模式
! NOTE! These had better be the same as in bootsect.s!
INITSEG = 0x9000 ! we move boot here - out of the way
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020 ! this is the current segment
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
entry start
start:
! ok, the read went well so we get current cursor position and save it for
! posterity.
;功能03H
;功能描述:在文本坐标下,读取光标各种信息
;入口参数:AH=03H
;BH=显示页码
;出口参数:CH=光标的起始行
;CL=光标的终止行
;DH=行(Y坐标)
;DL=列(X坐标)
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
;在bootset.S中已经设置过了相关寄存器
;将光标行列信息保存至0x9000:0处
mov [0],dx ! it from 0x90000.
! Get memory size (extended mem, kB)
;功能88H
;功能描述:读取扩展内存大小
;入口参数:AH=88H
;出口参数:AX=扩展内存字节数(以K为单位)
mov ah,#0x88
int 0x15
mov [2],ax
! Get video-card data:
;功能0FH
;功能描述:读取显示器模式
;入口参数:AH=0FH
;出口参数:AH=屏幕字符的列数
;AL=显示模式(参见功能00H中的说明)
;BH=页码
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
;功能12H
;功能描述:显示器的配置中断。其子功能说明如下:
;功能号 功能名称 功能号 功能名称
;10H — 读取配置信息 20H — 选择屏幕打印
;30H — 设置扫描行 31H — 允许/禁止装入缺省调色板
;32H — 允许/禁止显示 33H — 允许/禁止灰度求和
;34H — 允许/禁止光标模拟 35H — 切换活动显示
;36H — 允许/禁止屏幕刷新
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
! Get hd0 data
;直接从中断向量表中获取硬盘的参数
;中断向量是作为IDT的索引号,这里*4是代表向量0x41的线性地址(0x0000:0x104)
;从本内存地址复制到0x9000:0x0080处
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41] ;请参考 http://www.cnblogs.com/motadou/archive/2009/01/16/1376717.html
mov ax,#INITSEG
mov es,ax
mov di,#0x0080
mov cx,#0x10
rep
movsb
! 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
! Check that there IS a hd1 :-)
;On entry: AH 15h
; DL Drive number
;Returns: CF Set if error, else cleared
; AH Disk type (see below)
; Status of operation if error (see Service 01)
;CX:DX If no error and AH <> 0, then 4-byte integer
;with number of sectors.
//Disk Type
AH Meaning
00h Drive not present
01h Diskette, no change line available
02h Diskette, change line available
03h Fixed Disk /
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:
! now we want to move to protected mode ...
cli ! no interrupts allowed ! ;关中断 EFLAGS IF置0
;深入思考为什么关中断 在构建idt的时候为 实模式到保护模式的转换
! 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
! 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
! that was painless, now we enable A20 为了能访问1M以上的内存空间 得开启A20地址线
call empty_8042
mov al,#0xD1 ! command write
out #0x64,al ;out只能al ax
call empty_8042
mov al,#0xDF ! A20 on
out #0x60,al
call empty_8042
! 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.
;对于这一段的理解 请参考 http://snower.blog.51cto.com/2918921/557678
;附加 http://blog.csdn.net/zxvf/article/details/1245761
;默认情况下,IRQ对应的中断号是由BIOS初始化的,IRQ0~IRQ7对应着中断号0x8~0xF。
;而可编程表现在,我们可以将IRQ0对应的中断号修改成0~255中的任意一个数,
;我们设为INT_NUM,然后IRQ1~IRQ7对应的中断号就跟着变成INT_NUM+1~INT_NUM+7了。
;举个例子,我们将IRQ0对应的中断号改到0x10,那么IRQ1对应的中断号就是0x11、IRQ2的是0x12……IR7的是0x17。
;我们可以通过4个ICW1~ICW4可配置8259A,IRQ与中断号的对应就是通过ICW2(一个8位的数)来完成的。
;ICW2用来指示出IRQ0使用的中断号是什么,因为最后三位均是零,因此要求IRQ0的中断号必须是8的倍数,
;这又是一个很巧妙的设计。因为IRQ1的中断号就是IRQ0的中断号+1,IRQ2的中断号就是IRQ0的中断号+2,
;刚好填满一个8个的中断向量号空间。
//ICW1:发送到0x20(主片)及0xa0(从片)端口
//7 6 5 4 3 2 1 0//
//0 0 0 1 M 0 C I//
//I位:若置位,表示ICW4会被发送。(ICW4等下解释)
//C位:若清零,表示工作在级联环境下。
//M位:指出中断请求的电平触发模式,在PC机中,它应当被置零,表示采用“边沿触发模式”。
//ICW2:发送到0x21(主片)及0xa1(从片)端口
//7 6 5 4 3 2 1 0
//A7 A6 A5 A4 A3 0 0 0
//ICW3:发送到0x21(主片)及0xa1(从片)端口
//ICW3只有在级联工作的时候才会被发送,它主要用来建立两个PIC之间的连接,对于主片与从片,它结构是不一样的。
//(主片结构:)
//7 6 5 4 3 2 1 0
//IRQ7 IRQ6 IRQ5 IRQ4 IRQ3 IRQ2 IRQ1 IRQ0
//上面,如果相应的位被置1,则相应的IRQ线就被用于与从片连接,若清零则表示被连接到外围设备。
//(从片结构:)
//7 6 5 4 3 2 1 0
//0 0 0 0 0 IRQ
//上面的IRQ位指出了是主片的哪一个IRQ连到了从片,这需要同主片上发送的上面的主片结构字一致。
//ICW4:发送到0x21(主片)及0xa1(从片)端口
//7 6 5 4 3 2 1 0
//0 0 0 0 0 0 EOI 80x86
//80x86位:若置位则表示工作在80x86架构下。
//EOI位:若置位则表示自动清除中断请求信号。在PC上这位需要被清零。
//注意:只有在级联的方式的情况下才需要设置ICW3
mov al,#0x11 ! initialization sequence 表示中断请求边沿触发,多片级联并且租后需要发送ICW4
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
! 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! (建议使用 mov ax,cr0为了兼容以前的80286才使用lmsw)
;注意此处的cs=0x8已为保护模式下的段选择子
;这里使用的是GDT中的第二个段描述符
jmpi 0,8 ! jmp offset 0 of segment 8 (cs) 刷新当前的cpu列队
//setup.S完///
! 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
gdt:
;第一个段描述符 为全0 (规定)
.word 0,0,0,0 ! dummy(虚拟的 假的)
;第二个段描述符(内核代码段)
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
;第三个段描述符(内核数据段)
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
idt_48: ;(16*3=48)
.word 0 ! idt limit=0 idt的限长值
.word 0,0 ! idt base=0L IDT的基地址
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
.text
endtext:
.data
enddata:
.bss
endbss: