INITSEG = 0x9000 !原来bootsect所在的段
SYSSEG = 0x1000 !system所在的段
SETUPSEG= 0x9020 !setup所在的段
.globl begtext,begdata,begbss,endtext,enddata,endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
entry start
start:
mov ax,#INITSEG !将ds设置成0x9000
mov ds,ax !
mov ah,#0x03 !读取cursor pos.BIOS中断0x10读取光标功能号ah=0x03
!输入:bh = 页号
!返回:ch = 扫描开始线 cl=扫描结束线
! dh = 行号(0x00是顶端) dl=列好(0x00是左边)
xor bh,bh !
int 0x10 !
mov [0],dx !把光标位置信息存放在0x90000处,控制台初始化时来取
! get memory size(extended mem kb)下面三句取扩展内存的大小值
mov ah,#0x88 !调用中断0x15,功能号ah=0x88
int 0x15 !返回:ax=从0x100000(1M)处开始的扩展内存大小
!出错则CF置位,ax=出错码
mov [2],ax !将扩展内存存储在0x90002处
!get video-card data 下面这段用于取显卡当前显示功能号
mov ah,#0x0f !调用BIOS中断0x10,功能号ah=0x0f
!返回:ah=字符列数 al=显示模式 bh=当前显示页
int 0x10 !0x90004(1字)存放当前页,0x90006显示模式,0x90007字符列数
mov [4],bx !bh=display page
mov [6],ax !al=video mode ah=window width
!check for VGA/EGA and some config parameters检查显示方式并取参数
!掉用BIOS中断0x10
!功能号:ah=0x12 bl=0x10
mov ah,#0x12 !返回:bh=显示状态 (0x01-单色模式,I/O端口=0x3bx)
!(0x00-彩色模式 I/O端口=0x3dx)
mov bl,#0x10 !bl=安装的显示内存
!(0x00-64k,0x01-128k,0x02-192k,0x03-256k)
int 0x10 !cx=显卡特性参数
mov [8],ax !???
mov [10],bx !0x9000A=安装显卡内存,0x9000B=显示状态(彩色/单色)
mov [12],cx !0x9000c=显卡特性参数
!get hd0 data 取第一硬盘的信息。第一个硬盘的参数表的首地址是中断向量0x41的向量值!而
!第二个硬盘的参数表紧接着第一个表的后面,中断向量0x46的向量值也指向这第二个硬盘的参数首地!址。表的长度是16个字节
!下面两段程序分别复制BIOS有关两个硬盘的参数表,0x90080处存放第一个硬盘的表
!0x90090存放第二个硬盘的表
mov ax,#0x0000 !
mov ds,ax !
lds si,[4*0x41] !取中断向量0x41的值,也即hd0参数表的地址->ds:si
mov ax,#INITSEG !
mov es,ax !
mov di,#0x0080 !传输目的地址:0x9000:0x0080->es:di
mov cx,#0x10 !共传输0x10字节
rep !
movsb !
!
mov ax,#0x0000 !
mov ds,ax !
lds si,[4*0x46] !
mov ax,#INITSEG !
mov es,ax !
mov di,#0x0090 !
mov cx,#0x10 !
rep !
movsb !
!检查系统是否存在第二个硬盘,如果不存在则第二个硬盘表清零
!利用BIOS中断调用0x13的取盘类型功能
!功能号ah=0x15
!输入dl=驱动器号(0x8X是硬盘,0x80第一个硬盘,0x81第二个)
!输出:ah=类型码 00--没有这个盘 CF置位,01--软驱,没有change-line支持
!02--软驱(或其他科移动设备),有change-line支持,03--硬盘
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: !
cli !关中断
!首先将system模块移到正确位置.bootsect引导程序是将system读取到0x1000开始的位置
!当时假设system最大长度不会超过0x80000(512k),也即末端不会超过内存地址0x90000,所以
!bootsect将自己移动到0x90000开始的地方,并把setup加载到它的后面.下面程序是再把整个system
!移动到0x0000位置,即把从0x10000到0x8ffff的内存数据块,整块的向内存低端移动了0x10000(64K)
!的位置
mov ax,#0x0000 !
cld !向前移动。设置复制方向
do_move: !
mov es,ax !目的地址 es:di 0x0000:0x0
add ax,#0x1000 !
cmp ax,#0x9000 !已经移动完成?
jz end_move !
mov ds,ax !源地址 ds:si 0x1000:0x0
sub di,di !
sub,si,si !
mov cx,#0x8000 !移动0x8000字节
rep !
movsw !
jmp do_move !
!lidt指令用于加载中断描述符表寄存器,它的操作数是6个字节,0-1是描述符表的长度值
!(字节),2-5字节是描述符表的32位线性地址(首地址).中断描述符表中的每一个表项(8字节)
!指出发生中断时需要调用的代码的信息,与中断向量有些相似,但包含更多的信息
!lgdt指令用于加载全局描述符寄存器,其操作数格式与lidt指令相同。全局描述符表中的
!每个描述符项(8字节)描述了保护模式下的数据和代码段的信息。包括段的最大长度限制,段的线性!基址(32位)段特权级,段是否在内存,读写许可以及其他一些保护模式运行的标志
!
end_move:
mov ax,#SETUPSEG !
mov ds,ax !ds指向本程序段
lidt idt_48 !加载中断描述符寄存器idt_48是6字节操作数的位置.前2字节表示idt
!限长,后4字节表示idt表所处的基地址。
lgdt gdt_48 !加载全局描述符表寄存器
!
call empty_8042 !等待输入缓冲器空
mov al,#0xD1 !只有当缓冲器为空时才可以对其写命令
out 0x64,al !??8042的p2端口.p2端口的位1用于A20线选通.数据要写到0x60端口
call empty_8042 !等待输入缓冲器为空,看命令是否被接受
mov al,#0xDF !A20 on,选通A20地址线的参数
out #0x60,al !
call empty_8042 !输入缓冲器为空,表示A20线已经选通
mov al,#0x11 !0x11表示初始化命令开始,是ICW1命令字,表示边沿触发 多片级联 最
out #0x20,al !后要发送ICW4命令。发送到8259主片
!下面定义的两个字是直接使用机器码表示的两条相对跳转指令,起延时作用。
!0xeb是直接近跳转指令的操作码,带1个字节的相对位移值。因此跳转范围是-127到127.CPU通过吧
!这个相对位移值加到EIP寄存器中就形成一个新的有效地址,此时EIP指向下一条被执行的指令。执
!行时所花费的cpu时钟周期是7-10个。0x00eb表示跳转值是0的一条指令,因此还时直接执行下一条
!指令.这两条指令共可提供17-20个cpu时钟周期的延迟。另外每个空操作指令NOP的时钟周期是3个,
!因此要达到相同的延迟效果就需要6-7个NOP指令
! .word 0x00eb,0x00eb !jmp $+2,jmp$+2 $表示当前指令地址
out #0xA0,al !发送到8259从芯片
.word 0x00eb,0x00eb !
mov al,#0x20 !送主芯片ICW2命令字,起始中断号,要送奇地址
out #0x21,al !
.word 0x00eb,0x00eb !
mov al,#0x28 !送从芯片ICW2命令字,从芯片起始中断号
out #0xA1,al !
.word 0x00eb,0x00eb !
mov al,#0x04 !主芯片ICW3命令字,
out #0x21,al !
.word 0x00eb,0x00eb !
mov al,#0x02 !从芯片ICW3
out #0xA1,al !
.word 0x00eb,0x00eb !
mov al,#0x01 ! 主芯片ICW4
out #0x21,al !
.word 0x00eb,0x00eb !
out #0xA1,al !从芯片ICW4命令字
.word 0x00eb,0x0eb !
mov al,#0xff !屏蔽主芯片所有中断请求
out #0x21,al !
.word 0x00eb,0x00eb !
out #0xA1,al !屏蔽从片中断请求
!
mov ax,#0x0001 !保护模式比特位(PE)
lmsw ax !加载机器状态字
jmpi 0,8 !跳转至cs段8,偏移0处
!已经将system模块移动到0x00000开始地方,所以这里的偏移地址是0,这里的段值8已经是保护模式
!下的段选择符了,用于选择描述符表和描述符表项以及所要求的特权级。段选择符长度为16位,
!位0-1表示特权级,位2用于选择全局描述符表(0)还是局部描述符表(1),位3-15是描述符表项的
!索引,指出选择第几项描述符。所以段选择符8(0b0000 0000 0000 1000)表示特权级0,使用全局描!!述符表第一项,该项指出代码的基地址为0,因此这里的跳转指令就会去执行system代码
empty_8042: !
.word 0x00eb,0x00eb !
in al,#0x64 ! 读AT键盘控制器状态寄存器
test al,#2 !测试第二位,输入缓冲器满
jnz empty_8042 !
ret !
!全局描述表开始处,描述符表由多个8字节长的描述符表组成。
!这里给出了3个描述符项,第一个无用,但必须存在,第二个是系统代码段描述符,第三项是系统数!据段描述符
gdt: !
.word 0,0,0,0 !
!这里在gdt表中偏移为0x08,加载代码段寄存器时,使用的是这个偏移值
.word 0x07ff !8M -limit=2047
.word 0x0000 !base address=0
.word 0x9a00 !code read/exec
.word 0x00C0 !粒度(granularity)=4096,386
!这里在gdt中偏移值为0x10,加载数据段寄存器时使用这个偏移值
.word 0x07FF !8M limit
.word 0x0000 !base address
.word 0x9200 !data read/write
.word 0x00C0 !granularity = 4096 386
idt_48: !
.word 0 ! idt limit =0
.word 0,0 !idt base =0
gdt_48: !
.word 0x800 !gdt limit=2048,256 GDT entries
!全局表长度为2k字节,因每8字节组成一个段描述符项
!所以共有256项
.word 512+gdt,0x9 !gdt base = 0x9XXXX
!4个字节构成的内存线性地址0x0009<<16+0x0200+gdt
!也即0x90200+gdt
.text
endtext:
.data
enddata:
.bss
endbss:
setup运行完成后的内存示意图: