續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語言
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自作入門》川合秀實著,