天天看点

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自作入门》川合秀实著,

继续阅读