天天看點

1個人開發作業系統之C語言的開始

續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後結果如下:

1個人開發作業系統之C語言的開始

辛苦,辛苦

未完- 桌面與文字顯示

參考資料:

《OS自作入門》川合秀實著,

繼續閱讀