续1个人开发操作系统之初篇
本文任务是读取软盘18个Sector,编写video.s显示8bit,320*200黑屏,并进入32bit保护模式,编写func.s 和bootpack.c显示白屏,并用编写Makefile编译源文件。
1. boot.s读入18个sector
reading:
mov ax,0x0820
mov es,ax ;0x0820(es) * 16=0x8200 ;第二个Sector的数据读入到内存的0x8200地址。
mov ch,0 ;track/cylinder number
mov dh,0 ;head number
mov cl,2 ;sector number
readloop:
mov si,0 ; count failure times
retry:
;http://en.wikipedia.org/wiki/BIOS_interrupt_call#INT_13h_AH.3D02h:_Read_Sectors_From_Drive
mov ah,0x02 ;status of reading disk sector
mov al,1 ;number of sectors read
mov bx,0 ;0x0820(es) * 16 + 0(bx)=0x8200, 0x7e00~0x9fbff之间
mov dl,0x00 ;A drive
int 0x13 ;Read
jnc next ;no error goto next
add si,1 ;si +1
cmp si,5 ;compare 5
jae error ;goto error
mov ah,0x00 ;
mov dl,0x00 ; a drive
int 0x13 ; drive reset
jmp retry
next:
mov ax,es ; add 0x200(512)
add ax,0x20
mov es,ax
add cl,1 ;cl+1
cmp cl,18 ;compare 18,18 sectors
jbe readloop ;<18,continue
mov cl,1
add dh,1
cmp dh,2 ;读完正面18个Sector后,读反面18个
jb readloop ;dh<2 goto readloop
mov dh,0
add ch,1
cmp ch,CYLS
jb readloop ;ch<CYLS goto readloop
2. 调到video.s
;读完所有数据后,调到0x8200位置
fin:
mov [0x0ff0],ch ;remember the position of cylinder
JMP 0x8200 ;jump to video.s
hlt ;cpu停止
jmp fin
3. video.s
Colimas Simple OS的内存分布:
0x00000000-0x000fffff:启动时使用
0x00100000-0x00267fff:软盘数据备份
0x00268000-0x 0026f 7ff:空
0x 0026f 800-0x0026ffff:IDT
0x00270000-0x0027ffff:GDT
0x00280000-0x002ffffff:bootpack.img
0x00300000-0x003fffff:Stack以及其他
0x00400000- :空
;video.s
;Colimas Simple OS
BOTPAK EQU 0x00280000 ; the address of bootpack
DSKCAC EQU 0x00100000 ; Disk Cache Address of 32 mode address
DSKCAC0 EQU 0x00008000 ; Disk Cache of Real mode address
;Save the BOOT INFO to 0x0ff0
CYLS EQU 0x0ff0 ;save the boot info
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ;color info
SCRNX EQU 0x0ff4 ;pixel x
SCRNY EQU 0x0ff6 ;pixel y
VRAM EQU 0x0ff8 ;graphic buffer
org 0x8200
;http://en.wikipedia.org/wiki/BIOS_interrupt_call
mov al,0x13 ;video bios int, vga graphics,320*200*8bit color
mov ah,0x00
int 0x10 ;黑屏
mov BYTE [VMODE],8 ; save screen mode
mov WORD [SCRNX],320
mov WORD [SCRNY],200 ;8bit 320*200
mov DWORD [VRAM],0xa0000
;the led status of keybroad
mov ah,0x02
int 0x16 ; keybroad BIOS:Read Keyboard Shift Status
mov [LEDS],al
;block PIC
mov al,0xff
out 0x21,al
nop
out 0xa1,al
cli ;block cpu interrupt
;use >1mb memory set a20gate
call waitkbdout
mov al,0xd1
out 0x64,al ;write out port
call waitkbdout
mov al,0xdf ;enable a20
out 0x60,al
call waitkbdout
;enter protected mode
lgdt [GDTR0] ; init GDTR
mov eax,CR0
and eax,0x7fffffff ;set bit31 to 0( block paging)
or eax,0x00000001 ;set bit1 to 1( enable protect mode)
mov cr0,EAX
jmp pipelineflash
pipelineflash:
mov ax,1*8 ; reinit all the register from 0x0000 to 0x0008
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
; mov bootpack
mov esi,bootpack ; source
mov edi,BOTPAK ; destination
mov ecx,512*1024/4
call memcpy
;move boot sector
mov esi,0x 7c 00 ; source
mov edi,DSKCAC ; destination
mov ecx,512/4
call memcpy
;move others
mov esi,DSKCAC0+512 ;source
mov edi,DSKCAC+512 ;destination
mov ecx,0
mov cl,BYTE [CYLS]
imul ecx,512*18*2/4
sub ecx,512/4
call memcpy
;start bootpack
skip:
MOV ESP,0x00300000 ; init stack pointer
JMP DWORD 2*8:0x00
waitkbdout:
in al,0x64
and al,0x02
in al,0x60
jnz waitkbdout ; if result of and is not 0 goto waitkbdout
ret
memcpy:
mov eax,[esi]
add esi,4
mov [edi],eax
add edi,4
sub ecx,1
jnz memcpy
ret
alignb 16
GDT0: ;init gdtr
RESB 8 ; null sector
DW 0xffff,0x0000,0x9200,0x00cf ;
DW 0xffff,0x0000,0x 9a 28,0x0047 ;for bootpack
DW 0
GDTR0:
DW 8*3-1
DD GDT0
alignb 16
bootpack:
其中:
;use >1mb memory set a20gate
call waitkbdout
mov al,0xd1
out 0x64,al ;write out port
call waitkbdout
mov al,0xdf ;enable a20
out 0x60,al
call waitkbdout
代码的目的是屏蔽键盘和鼠标,并使CPU使用1MB以上的内存。60h和64h端口是向Keyboard和mouse发送和接收指令。
lgdt [GDTR0] ; init GDTR
初始化GDTR,为了使用32Bit模式,并防止多任务处理时内存访问冲突,提出Segmentation,就是将内存分割为块,每块称为Segment,其初始地址都为0,这样一来,程序就不再需要ORG指令,来指定运行程序的内存初始位置了。
每个Segment需要以下信息:
l Segment大小
l Segment地址
l Segment属性(禁止写,禁止执行,或者系统专用等)
本程序初始化2个Segment,第一个为只读Segment,地址为0x000000,
第二个为可执行Segment,用来执行bootpack.c程序,地址为0x280000
DW 0xffff,0x0000,0x9200,0x00cf ;
DW 0xffff,0x0000,0x 9a 28,0x0047 ;for bootpack
mov eax,CR0
and eax,0x7fffffff ;set bit31 to 0( block paging)
or eax,0x00000001 ;set bit1 to 1( enable protect mode)
mov cr0,EAX
设置CR0寄存器,进入保护模式。
mov ax,1*8 ; reinit all the register from 0x0000 to 0x0008
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
是将所有寄存器都进入第二个Segement
下面程序之前的代码都是将软盘的数据都保存到内存中。
;start bootpack
skip:
MOV ESP,0x00300000 ; init stack pointer
JMP DWORD 2*8:0x00
程序调到0x280000位置执行bootpack.c的程序。
4.bootpack.c和func.s,进入C语言
/*Colimas Simple OS*/
void io_hlt(void);
void write_mem8(int addr, int data);
//entry
void ColimasMain(void)
{
int i;
for(i=0xa0000;i<=0xaffff;i++){
write_mem8(i,15);
}
for(;;)
io_hlt();
}
//0xa0000到0xaffff是显卡内存地址。15代表白色。函数write_mem8功能是将0xa0000到0xaffff地址间的内存赋赋值为15,即白色。
for(i=0xa0000;i<=0xaffff;i++){
write_mem8(i,15);
}
for(;;)
io_hlt();
为死循环
在func.s内实现write_mem8和io_hlt函数。
;func.s
;Colimas Simple OS
[BITS 32]
segment .text
GLOBAL _io_hlt,_write_mem8
_io_hlt:
hlt
ret
_write_mem8: ;void write_mem8(int addr,int data);
mov ecx,[esp+4] ;addr
mov al,[esp+8] ;data
mov [ecx],al
RET
5. Makefile
MAKE = make -r
NASM = nasm
COPY = cp
LINK = olink
LD = ld
CAT = cat
DEL = rm
CC = gcc
OBJCOPY = objcopy
default :
$(MAKE) img
bin : boot.s Makefile
$(NASM) boot.s -o boot.bin
sys : video.s objcopy Makefile
$(NASM) video.s -o video.sys
$(CAT) video.sys bootpack.img > video.img
bootpack : bootpack.c Makefile
$(CC) -c bootpack.c
func :func.s Makefile
$(NASM) -f coff func.s -l func.lst
objcopy : bootpack func Makefile
$(LD) bootpack.o func.o -o bootpack.bin
$(OBJCOPY) bootpack.bin -O binary bootpack.img
img : bin sys Makefile
$(LINK) -m boot.bin video.img -o boot.img
compile :
$(MAKE) img
run :
$(MAKE) compile
$(COPY) boot.img ../qemu/
clean :
-$( DEL ) *.bin
-$( DEL ) *.sys
-$( DEL ) *.img
-$( DEL ) *.o
汇编编译器Nasm
C语言编译器为GCC
连接器为ld
纯二进制转换器为objcopy
运行make命令后,编译生成boot.img, 使用Qemu运行boot.img后结果如下:
辛苦,辛苦
未完- 桌面与文字显示
参考资料:
《OS自作入门》川合秀实著,