如何编写自己的操作系统(1)

本文转载自: https://www.cnblogs.com/wangshuo/archive/2011/11/26/2264540.html 作者:wangshuo 转载请注明该声明。

      我也是自己一步一步学着别人的方法来编写一个操作系统。把一些重点记下来。教程在这里:http://www.brokenthorn.com/Resources/OSDevIndex.html

 

1、准备工作

      一台32位Intel的电脑就可以了,使用Windows操作系统。然后下载一些软件安装上:

 

软件名下载地址说明
NASMhttp://nasm.sourceforge.net/nasm –f bin Boot4.asm –o Boot4.bin
PartCopyhttp://www.brokenthorn.com/Resources/Programs/pcopy02.zippartcopy Boot4.bin 0 200 -f0

VFD - Virtual Floppy Drive

http://sourceforge.net/projects/vfd/

How to use VFD

Bochs Emulator

http://bochs.sourceforge.net/

How to use Bochs

 

      NASM和PartCopy需要设置一下环境变量,在Path中添加指向其.exe目录的文件夹即可。其它问题如果有什么不明白的,可以参看这里

   VFD启动时注意使用管理员权限启动,否则会报“没有权限”的错误。

2、Bootloader

      好了,我们直接进入启动程序。整个程序如下:

Boot4.asm;*********************************************
;	Boot1.asm
;		- A Simple Bootloader
;*********************************************

org 0
bits 16

start:
        jmp main
        
;*********************************************
;	BIOS Parameter Block
;*********************************************

; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size.
; If you use a short jump, add a "nop" after it to offset the 3rd byte.

bpbOEM			db "My OS "			; OEM identifier (Cannot exceed 8 bytes!)
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	DB 2
bpbRootEntries: 	DW 224
bpbTotalSectors: 	DW 2880
bpbMedia: 		DB 0xf8  ;; 0xF1
bpbSectorsPerFAT: 	DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 	DD 0
bpbTotalSectorsBig:     DD 0
bsDriveNumber: 	        DB 0
bsUnused: 		DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3   ; will be overwritten
bsVolumeLabel: 	        DB "MOS FLOPPY "
bsFileSystem: 	        DB "FAT12 "

;*********************************************
;	Prints a string
;       DS=>SI: 0 terminated string
;   Changed Register
;       AX, SI
;*********************************************

Print:
        lodsb
        or al, al
        jz PrintDone
        mov ah, 0eh
        int 10h
        jmp Print
PrintDone:
        ret
        
;************************************************;
; Reads a series of sectors
; Input:
;       CX=>Number of sectors to read
;       AX=>Starting sector (logical block addressing)
;       ES:BX=>Buffer to read to
; Changed:
;       DI, SI, AX, CX, BX
;************************************************;

ReadSectors:
    .MAIN:
        mov di, 0x0005          ; five retries for error
    .SECTORLOOP:
        push ax
        push bx
        push cx
        call LBACHS             ; compute absoluteTrack, absoluteSector, absoluteHead
        mov ah, 0x02            ; BIOS read sector
        mov al, 0x01            ; read one sector
        mov ch, BYTE [absoluteTrack]
        mov cl, BYTE [absoluteSector]
        mov dh, BYTE [absoluteHead]
        mov dl, BYTE [bsDriveNumber]
        int 0x13                ; invoke BIOS
        jnc .SUCCESS            ; test for read error. CF=0 then jump
        xor ax, ax              ; BIOS reset disk
        int 0x13
        dec di
        pop cx
        pop bx
        pop ax
        jnz .SECTORLOOP
        int 0x18
    .SUCCESS:
        mov si, msgProgress
        call Print
        pop cx
        pop bx
        pop ax
        add bx, WORD [bpbBytesPerSector]            ; queue next buffer
        inc ax                                      ; queue next sector
        loop .MAIN                                  ; read next sector. Controlled by CX, If CX=0, then stop
        ret
        
;************************************************;
; Convert CHS to LBA
; Input:
;       AX=>the cluster to be changed
; Changed:
;       AX, CX
; Return:
;       AX=>sector number
; LBA = (cluster - 2) * sectors per cluster
;************************************************;

ClusterLBA:
        sub ax, 0x0002                                ; zero base cluster number
        xor cx, cx
        mov cl, BYTE [bpbSectorsPerCluster]           ; convert byte to word
        mul cx
        add ax, WORD [datasector]                     ; base data sector
        ret
        
;************************************************;
; Convert LBA to CHS
; Input:
;       AX=>LBA Address to convert
; Changed:
;       DX, AX
; Return:
;       BYTE [absoluteSector], BYTE [absoluteHead], BYTE [absoluteTrack]
;
; absolute sector = (logical sector % sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
;
;************************************************;

LBACHS:
        xor dx, dx          ; prepare dx:ax for operation
        div WORD [bpbSectorsPerTrack]
        inc dl              ; adjust for sector 0
        mov BYTE [absoluteSector], dl
        xor dx, dx
        div WORD [bpbHeadsPerCylinder]
        mov BYTE [absoluteHead], dl
        mov BYTE [absoluteTrack], al
        ret
        
;*********************************************
;	Bootloader Entry Point
;*********************************************

main:
    
    ;-----------------------------------------------------
    ; code located at 0000:7c00, adjust segment registers
    ;-----------------------------------------------------
    
        cli
        mov ax, 0x07c0          ; setup registers to point to our segment. s*16+off = address
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        
    ;-----------------------------------------------------
    ; create stack
    ;-----------------------------------------------------
    
        mov ax, 0x0000          ; set the stack
        mov ss, ax
        mov sp, 0xffff
        sti                     ; restore interrupts
        
    ;-----------------------------------------------------
    ; display loading message
    ;-----------------------------------------------------
    
        mov si, msgLoading      ; "Loading Boot Image "
        call Print
        
    ;-----------------------------------------------------
    ; load root directory table
    ;-----------------------------------------------------
    
    LOAD_ROOT:
    
    ; compute size of root directory and store in "cx"
    
        xor cx, cx                                
        xor dx, dx
        mov ax, 0x0020                            ; 32 bytes directory entry
        mul WORD [bpbRootEntries]                 ; total size of directory. bpbTotalSectors = 2880
        div WORD [bpbBytesPerSector]              ; sectors used by directory. ax is the consult
        xchg ax, cx                               ; now cx is the result, ax is 0x0000
        
    ; compute location of root directory and store in "ax"
    
        mov al, BYTE [bpbNumberOfFATs]
        mul WORD [bpbSectorsPerFAT]
        add ax, WORD[bpbReservedSectors]
        mov WORD [datasector], ax                 ; base of root directory
        add WORD [datasector], cx                 ; ?
        
    ; read root directory into memory (7c00:0200)
    
        mov bx, 0x0200
        call ReadSectors
        
    ;------------------------------------------------
    ; Find stage 2
    ;------------------------------------------------
    
    ; browse root directory for binary image
        
        mov cx, WORD [bpbRootEntries]
        mov di, 0x0200
        
    .LOOP:
        push cx
        mov cx, 0x000b              ; eleven character name
        mov si, ImageName           ; image name to find
        push di
        rep cmpsb                   ; test for entry match
        pop di
        je LOAD_FAT                 ; if found, "DI" is the pointer to ImageName in the Root Directory
        pop cx
        add di, 0x0020              ; queue next directory entry. Each entry in Root Directory is 32 bytes (0x20)
        loop .LOOP                  ; cx = bpbRootEntries, check "cx" times.
        jmp FAILURE
        
    ;----------------------------------------------
    ; load FAT
    ;----------------------------------------------
    
    LOAD_FAT:
    
    ; save starting cluster of boot image
    
        mov si, msgCRLF
        call Print
        mov dx, WORD [di + 0x001a]          ; di contains starting address of entry. Just refrence byte 26 (0x1A) of entry
        mov WORD [cluster], dx              ; file's first cluster
        
    ; compute size of FAT and store in "cx"
    
        xor ax, ax
        mov al, BYTE [bpbNumberOfFATs]
        mul WORD [bpbSectorsPerFAT]
        mov cx, ax
        
    ; compute location of FAT and store in "ax"
    
        mov ax, WORD [bpbReservedSectors]       ; adjust for bootsector
        
    ; read FAT into memory (7c00:0200)
    
        mov bx, 0x0200
        call ReadSectors
        
    ; read image file into memory (0050:0000)
    
        mov si, msgCRLF
        call Print
        mov ax, 0x0050
        mov es, ax
        mov bx, 0x0000
        push bx
        
    ;----------------------------------------------
    ; load stage 2
    ;----------------------------------------------
    
    LOAD_IMAGE:
        
        mov ax, WORD [cluster]              ; cluster to read. File's first cluster
        pop bx                              ; buffer to read into. ES:BX. es=0x0050
        call ClusterLBA                     ; convert cluster to LBA
        xor cx, cx
        mov cl, BYTE [bpbSectorsPerCluster]
        call ReadSectors
        push bx
        
    ; compute next cluster
    
        mov ax, WORD [cluster]          ; identify current cluster
        mov cx, ax                      ; copy current cluster
        mov dx, ax
        shr dx, 0x0001                  ; divide by two
        add cx, dx                      ; sum for (3/2)
        mov bx, 0x0200                  ; location of FAT in memory
        add bx, cx                      ; index into FAT
        mov dx, WORD [bx]               ; read two bytes from FAT
        test ax, 0x0001
        jnz .ODD_CLUSTER
        
    .EVEN_CLUSTER:
    
        and dx, 0000111111111111b       ; take low twelve bits
        jmp .DONE
        
    .ODD_CLUSTER:
    
        shr dx, 0x0004                  ; take high twelve bits
        
    .DONE:
    
        mov WORD [cluster], dx          ; store new cluster
        cmp dx, 0x0ff0                  ; test for end of file
        jb LOAD_IMAGE
        
    DONE:
    
        mov si, msgCRLF
        call Print
        push WORD 0x0050
        push WORD 0x0000
        retf                            ; jmp to 0x0050:0000 to excute (MAY BE)
        
    FAILURE:
    
        mov si, msgFailure
        call Print
        mov ah, 0x00
        int 0x16                ; a wait keypress
        int 0x19                ; warm boot computer
        
    absoluteSector db 0x00
    absoluteHead db 0x00
    absoluteTrack db 0x00
    
    datasector dw 0x0000
    cluster dw 0x0000
    ImageName db "KRNLDR SYS"
    msgLoading db 0x0d, 0x0a, "Loading Boot Image ", 0x0d, 0x0a, 0x00
    msgCRLF db 0x0d, 0x0a, 0x00
    msgProgress db ".", 0x00
    msgFailure db 0x0d, 0x0a, "ERROR : Press Any Key to Reboot", 0x0a, 0x00
    
        TIMES 510-($-$$) db 0   ; confirm the compiled bin file is 512B
        dw 0xaa55               ; the bootable special character

 

 

      这个程序经过NASM编译之后会形成一个大小恰好为512B的文件,我们使用下面的命令来编译这个名为Boot4.asm的文件(为什么叫Boot4.asm?因为测试这个程序时正好是这个编号:)

nasm -f bin Boot4.asm -o Boot4.bin

 

      启动VFD,创建一个虚拟软盘,命名为A盘。然后使用PartCopy把Boot4.bin这个文件拷贝到软盘的第一个扇区:

partcopy Boot4.bin 0 200 –f0


      现在在软盘的第一个扇区就是我们的这个启动程序。计算机启动时会按顺序检查BIOS设定的所有启动设备(比如按照软驱、光驱、磁盘的顺序来检测是否在其中有可以启动的设备)。在这里,我们把Boot4.bin写入了磁盘的第一个扇区(磁盘的每个扇区为512B),并且这个文件的末尾为0xaa55,这个特殊的字节序列表示这是一个可以启动的文件。BIOS就把这个文件放到内存的 0x7c00:0 这个地址,去执行这个文件。大致过程可以参看这里(计算机按下电源后发生了什么)

      有关这个汇编程序Boot4.asm的详细解释我们后面再做。下面我们编写一个超级简单的操作系统Stage2.asm

3、一个超级简单的操作系统Stage2.asm

      不用任何解释,直接给出这个操作系统的代码。它的主要功能就是在屏幕上打印出 “Preparing to load operating system...”这个字符串

Stage2.asm; Note: Here, we are executed like a normal COM program, but we are still in
; Ring 0. We will use this loader to set up 32 bit mode and basic exception
; handling

; This loaded program will be our 32 bit kernal.

; We do not have the limitation of 512 bytes here, so we can add anything we
; want here!

org 0x0             ; offset to 0, we will set segments later
bits 16             ; we are still in real mode

; we are loaded at linear address 0x10000

jmp main

;*********************************
;   Prints a String
;   DS=>SI: 0 terminated string
;*********************************

Print:
        lodsb
        or al, al
        jz PrintDone
        mov ah, 0eh
        int 10h
        jmp Print
PrintDone:
        ret
        
;********************************
; Second Stage Loader Entry Point
;********************************

main:
        cli
        push cs
        pop ds
        
        mov si, Msg
        call Print
        
        cli
        hlt
        
;********************************
; Data section
;********************************

Msg db "Preparing to load operating system...",13,10,0

 

      之所以把这个文件叫做Stage2.asm,是因为这是系统启动的第二个阶段,这个操作系统是由Boot4.bin从磁盘中读取出来并且加载到内存中的这个文件会被加载到0x7c00:0x0200这个内存地址上。现在我们使用NASM把这个文件编译成一个二进制文件:

nasm -f bin Stage2.asm KRNLDR.SYS

 

 

 

 

 

      之所以把它编译成为KRNLDR.SYS,是因为在Boot4.asm中,我们设定了 ImageName db "KRNLDR  SYS" 这个语句。现在只要知道有这么回事就可以了。这个文件名不能随便改。

      下面我们把KRNLDR.SYS拷贝到磁盘A中:

copy KRNLDR.SYS A:\

 

      这时候,检查A盘,就会发现里面多出了一个KRNLDR.SYS这个文件。

4、设置模拟器

      下面我们使用Bochs这个模拟器来模拟系统的启动。首先安装这个模拟器。然后建立一个文件,名字叫做bochsrc.bxrc,里面的内容为:

bochsrc.bxrc# ROM and VGA BIOS images ---------------------------------------------
 
romimage:    file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest 
 
# boot from floppy using our disk image -------------------------------
 
floppya: 1_44=a:, status=inserted  # Boot from drive A
 
# logging and reporting -----------------------------------------------
 
#log:         OSDev.log             # All errors and info logs will output to OSDev.log
#error:       action=report 
#info:        action=report


      保存好后,运行这个文件,就可以看到模拟器启动了。最后稳定之后的界面应该是这个样子的:

image

      好了,到现在为止,我们的操作系统就已经运行完成了,打印出了一个字符串。如果你忘了把KRNLDR.SYS文件拷贝到A盘,它还会提示你出错。

      按一下这个界面上面的Power键,就可以结束这次模拟了。

 

      好了,有关代码的具体介绍,请参看这里

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值