一个启动扇区的空间太小了,我们需要更广阔的的空间于是我们加入了setup,我们会在引导扇区中将setup拷贝到内存,然后跳到setup中去执行,不过为了简单我们的第一个setup会非常简单,仅仅打印一句话
代码如下:
.text
.globl start/*程序从start处开始运行*/
.code16
start:
jmpl $0x07c0, $code
msg:
.string "I'm setup!"
code:
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%ss
mov $0x400,%sp
call DispStr/*调用显示字符串函数*/
loop0:/*无限循环*/
jmp loop0
DispStr:
mov $msg ,%ax
mov %ax ,%bp/*es:bp = 串地址*/
mov $10 ,%cx/*cs = 串长度*/
mov $0x1301,%ax/*ah=13是功能号表示显示字符串 ,al=01是显示输出方式*/
mov $0x000c,%bx/*bh=0是0页,bl=0ch高亮 黑底红字*/
mov $0x0 ,%dl/*0行0列*/
mov $0x1 ,%dh
int $0x10
ret
.org 0x7fe, 0x90
.word 0xaa55
在Makefile中我们将setup放在启动扇区的后面,给他预留了4个扇区的空间,我们假设代码不会超过512*4字节,其实我们也真的不会超过这个数。
Makefile如下:
AS =as
LD =ld
LDFLAGS = --oformat binary -N -e start -Ttext 0x0
Image:bootsect setup
cat bootsect setup >Image
bootsect:bootsect.s
$(AS) -o bootsect.o -a bootsect.s
$(LD) $(LDFLAGS) -o bootsect bootsect.o
setup:setup.s
$(AS) -o setup.o -a setup.s
$(LD) $(LDFLAGS) -o setup setup.o
clean:
rm -f Image setup bootsect *.o
下面我就修改启动扇区代码将setup读出并放到0x90200处
SETUPLEN = 4
BOOTSEG = 0x7c0
INITSEG = 0x9000
SETUPSEG = 0x9020
SYSSEG = 0x1000
ENDSEG = SYSSEG + SYSSIZE
.text
.globl start/*程序从start处开始运行*/
.code16
start:
mov $BOOTSEG,%ax //将ds段寄存器设置为0x07c0
mov %ax,%ds
mov $INITSEG,%ax //将es段寄存器设置为0x9000
mov %ax,%es
mov $256,%cx
sub %si,%si
sub %di,%di
rep
movsw
jmpl $INITSEG,$code
msg:
.string "Hello world!"
code:
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%ss
mov $0xff00,%sp
call DispStr/*调用显示字符串函数*/
load_setup:
movb $0x00,%dh//磁头号0
movb $0x00,%dl//驱动器号0
mov $0x0002,%cx
mov $0x0200,%bx//es:bx指向缓冲区
movb $0x02,%ah//0x02,读磁盘扇区到内存
movb $SETUPLEN,%al//需要读出的扇区数量
int $0x13
jnc ok_load_setup//进位标志CF为0时就跳转
mov $0x0000,%dx
mov $0x0000,%ax
int $0x13
jmp load_setup
ok_load_setup:
jmpl $SETUPSEG,$0
loop0:/*无限循环*/
jmp loop0
DispStr:
mov $msg ,%ax
mov %ax ,%bp/*es:bp = 串地址*/
mov $12 ,%cx/*cs = 串长度*/
mov $0x1301,%ax/*ah=13是功能号表示显示字符串 ,al=01是显示输出方式*/
mov $0x000c,%bx/*bh=0是0页,bl=0ch高亮 黑底红字*/
mov $0 ,%dl/*0行0列*/
int $0x10
ret
下面我们就在setup中进入保护模式,最后两行代码是不足512*4-2字节的地方用0x90填充,最后的0xaa55并没有什么特殊的意义只不过为了调试方便而已。
#define Descriptor(base,lim,attr)\
.word lim&0xffff;\
.word base&0xffff;\
.byte (base>>16)&0xff;\
.word ((lim>>8)&0xf00)|(attr&0x0f0ff);\
.byte ((base>>24)&0xff)
/*
*InitDescrptor(Descriptor,SegBase)初始化描述符函数
*Descriptor:要初始化的描述符
*SegBase:段基址
*/
#define InitDescrptor(Descriptor,SegBase)\
xor %eax,%eax; \
mov %cs,%ax ; \
shl $4,%eax ; \
addl $(SegBase), %eax ;\
movw %ax, (Descriptor + 2);\
shr $16, %eax;\
movb %al, (Descriptor + 4);\
movb %ah, (Descriptor + 7)
DA_32=0x4000 //32位模式
DA_LIMIT_4K=0x8000 //颗粒度为4096
DA_DRW=0x92 //数据段可读可写
DA_CR=0x9A //可读可执行
DA_C = 0x98
SETUPSEG = 0x9020
SETUPAddr = SETUPSEG<<4
.text
.globl start/*程序从start处开始运行*/
.code16
start:
jmp code
msg:
.string "I'm setup!"
/**-----------------------------------------------------------------
* Global Descriptor Table: GDT
*-------------------------------*/
GDT_START:
Descriptor_DUMMY:Descriptor(0x0,0x0,0x0)
Descriptor_CODE32 :Descriptor(SETUPAddr,0xffffffff,DA_C+DA_32)
Descriptor_VIDEO:Descriptor(0xb8000,0x0ffff,DA_DRW)
GDT_END:
GdtPtr:
.word (GDT_END-GDT_START)-1 # so does gdt
.long GDT_START+SETUPAddr # This will be rewrite by code.
.set selector_Code32,(Descriptor_CODE32-GDT_START)
.set Selector_Video,(Descriptor_VIDEO-GDT_START)
code:
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%ss
mov $0x400,%sp
call DispStr/*调用显示字符串函数*/
/*初始全局描述符Descriptor_CODE32*/
InitDescrptor(Descriptor_CODE32,LABEL_SEG_CODE32);
/*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/
lgdt GdtPtr
/*关中断*/
cli
/*打开地址线A20*/
inb $0x92,%al
or $0x02,%al
outb %al,$0x92
/*设置cr0寄存器,切换到保护模式*/
movl %cr0,%eax
or $1,%eax
movl %eax,%cr0
/*真正进入保护模式,执行此命令后CS=0x8,IP=LABEL_SEG_CODE32的偏移地址*/
//ljmp $0x8,$(LABEL_SEG_CODE32)
ljmp $selector_Code32,$0
/*此时CS:IP=全局描述符表中第1(0x8>>3)项描述符给出的段基址+LABEL_SEG_CODE32的偏移地址*/
LABEL_SEG_CODE32:
.align 32
.code32
movw $0x10,%ax
movw %ax,%gs
movl $((80*11+79)*2),%edi/*第11行,79列*/
movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/
movb $'P',%al/*显示的字符*/
movw %ax,%gs:(%edi)
loop0:/*无限循环*/
jmp loop0
DispStr:
.align 16
.code16
mov $msg ,%ax
mov %ax ,%bp/*es:bp = 串地址*/
mov $10 ,%cx/*cs = 串长度*/
mov $0x1301,%ax/*ah=13是功能号表示显示字符串 ,al=01是显示输出方式*/
mov $0x000c,%bx/*bh=0是0页,bl=0ch高亮 黑底红字*/
mov $0x0 ,%dl/*0行0列*/
mov $0x1 ,%dh
int $0x10
ret
.org 0x7fe, 0x90
.word 0xaa55