Chinaunix首页 | 论坛 | 博客
  • 博客访问: 488996
  • 博文数量: 164
  • 博客积分: 4024
  • 博客等级: 上校
  • 技术积分: 1580
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-10 16:27
文章分类

全部博文(164)

文章存档

2011年(1)

2010年(108)

2009年(55)

我的朋友

分类:

2010-06-23 16:19:57

文件: MyOS3.rar
大小: 571KB
下载: 下载
在第二阶段,提出loader主要就是为了在内核运行之前完成:加载内核入内存  跳入保护模式
因为本人脑子有点笨 所以就一点点来吧 先完成加载内核入内存
看书上说内核的格式是ELF的,所以呢俺就装了nasm for linux为了把内核编译成elf格式。
 
首先改loader吧,loader主要是加载内存,和第二阶段扇区加载loader过程一样,只是文件名和内存地址不同,来看看:


org 0100h

;================================================================================================
BaseOfStack        equ    0100h

BaseOfKernelFile    equ     08000h    ; KERNEL.BIN 被加载到的位置 ---- 段地址
OffsetOfKernelFile    equ     0h    ; KERNEL.BIN 被加载到的位置 ---- 偏移地址

;================================================================================================

    jmp    LABEL_START        ; Start

; 下面是 FAT12 磁盘的头, 之所以包含它是因为下面用到了磁盘的一些信息
%include    "fat12hdr.inc"


LABEL_START:            ; <--- 从这里开始 *************
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, BaseOfStack

    mov    dh, 0            ; "Loading "
    call    DispStr            ; 显示字符串

    ; 下面在 A 盘的根目录寻找 KERNEL.BIN
    mov    word [wSectorNo], SectorNoOfRootDirectory    
    xor    ah, ah    ;
    xor    dl, dl    ; ┣ 软驱复位
    int    13h    ;
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    cmp    word [wRootDirSizeForLoop], 0    ;
    jz    LABEL_NO_KERNELBIN        ; ┣ 判断根目录区是不是已经读完, 如果读完表示没有找到 KERNEL.BIN
    dec    word [wRootDirSizeForLoop]    ;
    mov    ax, BaseOfKernelFile
    mov    es, ax            ; es <- BaseOfKernelFile
    mov    bx, OffsetOfKernelFile    ; bx <- OffsetOfKernelFile    于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFile
    mov    ax, [wSectorNo]        ; ax <- Root Directory 中的某 Sector 号
    mov    cl, 1
    call    ReadSector

    mov    si, KernelFileName    ; ds:si -> "KERNEL BIN"
    mov    di, OffsetOfKernelFile    ; es:di -> BaseOfKernelFile:???? = BaseOfKernelFile*10h+????
    cld
    mov    dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:
    cmp    dx, 0                    ;
    jz    LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    ; ┣ 循环次数控制, 如果已经读完了一个 Sector, 就跳到下一个 Sector
    dec    dx                    ;
    mov    cx, 11
LABEL_CMP_FILENAME:
    cmp    cx, 0            ;
    jz    LABEL_FILENAME_FOUND    ; ┣ 循环次数控制, 如果比较了 11 个字符都相等, 表示找到
    dec    cx            ;
    lodsb                ; ds:si -> al
    cmp    al, byte [es:di]    ; if al == es:di
    jz    LABEL_GO_ON
    jmp    LABEL_DIFFERENT
LABEL_GO_ON:
    inc    di
    jmp    LABEL_CMP_FILENAME    ;    继续循环

LABEL_DIFFERENT:
    and    di, 0FFE0h        ; else┓    这时di的值不知道是什么, di &= e0 为了让它是 20h 的倍数
    add    di, 20h            ;
    mov    si, KernelFileName    ;di += 20h 下一个目录条目
    jmp    LABEL_SEARCH_FOR_KERNELBIN;

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    add    word [wSectorNo], 1
    jmp    LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_KERNELBIN:
    mov    dh, 2            ; "No KERNEL."
    call    DispStr            ; 显示字符串
%ifdef    _LOADER_DEBUG_
    mov    ax, 4c00h        ;
    int    21h            ; ┛没有找到 KERNEL.BIN, 回到 DOS
%else
    jmp    $            ; 没有找到 KERNEL.BIN, 死循环在这里
%endif

LABEL_FILENAME_FOUND:            ; 找到 KERNEL.BIN 后便来到这里继续
    mov    ax, RootDirSectors
    and    di, 0FFF0h        ; di -> 当前条目的开始

    push    eax
    mov    eax, [es : di + 01Ch]        ;
    mov    dword [dwKernelSize], eax    ; ┛保存 KERNEL.BIN 文件大小
    pop    eax

    add    di, 01Ah        ; di -> 首 Sector
    mov    cx, word [es:di]
    push    cx            ; 保存此 Sector 在 FAT 中的序号
    add    cx, ax
    add    cx, DeltaSectorNo    ; 这时 cl 里面是 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)
    mov    ax, BaseOfKernelFile
    mov    es, ax            ; es <- BaseOfKernelFile
    mov    bx, OffsetOfKernelFile    ; bx <- OffsetOfKernelFile    于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFile
    mov    ax, cx            ; ax <- Sector 号

LABEL_GOON_LOADING_FILE:
    push    ax            ;
    push    bx            ;
    mov    ah, 0Eh            ; ┃ 每读一个扇区就在 "Loading " 后面打一个点, 形成这样的效果:
    mov    al, '.'            ;
    mov    bl, 0Fh            ; ┃ Loading ......
    int    10h            ;
    pop    bx            ;
    pop    ax            ;

    mov    cl, 1
    call    ReadSector
    pop    ax            ; 取出此 Sector 在 FAT 中的序号
    call    GetFATEntry
    cmp    ax, 0FFFh
    jz    LABEL_FILE_LOADED
    push    ax            ; 保存 Sector 在 FAT 中的序号
    mov    dx, RootDirSectors
    add    ax, dx
    add    ax, DeltaSectorNo
    add    bx, [BPB_BytsPerSec]
    jmp    LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

    call    KillMotor        ; 关闭软驱马达

    mov    dh, 1            ; "Ready."
    call    DispStr            ; 显示字符串

    jmp    $


;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop    dw    RootDirSectors    ; Root Directory 占用的扇区数
wSectorNo        dw    0        ; 要读取的扇区号
bOdd            db    0        ; 奇数还是偶数
dwKernelSize        dd    0        ; KERNEL.BIN 文件大小

;============================================================================
;字符串
;----------------------------------------------------------------------------
KernelFileName        db    "KERNEL BIN", 0    ; KERNEL.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength        equ    9
LoadMessage:        db    "Loading "
Message1        db    "Ready. "
Message2        db    "No KERNEL"
;============================================================================

;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;    显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:
    mov    ax, MessageLength
    mul    dh
    add    ax, LoadMessage
    mov    bp, ax            ;
    mov    ax, ds            ;ES:BP = 串地址
    mov    es, ax            ;
    mov    cx, MessageLength    ; CX = 串长度
    mov    ax, 01301h        ; AH = 13, AL = 01h
    mov    bx, 0007h        ; 页号为0(BH = 0) 黑底白字(BL = 07h)
    mov    dl, 0
    add    dh, 3            ; 从第 3 行往下显示
    int    10h            ; int 10h
    ret
;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;    从序号(Directory Entry 中的 Sector 号)ax 的的 Sector 开始,cl 个 Sector 读入 es:bx 中
ReadSector:
    ; -----------------------------------------------------------------------
    ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
    ; -----------------------------------------------------------------------
    ; 设扇区号为 x
    ; ┌ 柱面号 = y >> 1
    ; x ┌ 商 y ┤
    ; -------------- => ┤ └ 磁头号 = y & 1
    ; 每磁道扇区数 │
    ; └ 余 z => 起始扇区号 = z + 1
    push    bp
    mov    bp, sp
    sub    esp, 2            ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

    mov    byte [bp-2], cl
    push    bx            ; 保存 bx
    mov    bl, [BPB_SecPerTrk]    ; bl: 除数
    div    bl            ; y 在 al, z 在 ah
    inc    ah            ; z ++
    mov    cl, ah            ; cl <- 起始扇区号
    mov    dh, al            ; dh <- y
    shr    al, 1            ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
    mov    ch, al            ; ch <- 柱面号
    and    dh, 1            ; dh & 1 = 磁头号
    pop    bx            ; 恢复 bx
    ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
    mov    dl, [BS_DrvNum]        ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
    mov    ah, 2            ;
    mov    al, byte [bp-2]        ;al 个扇区
    int    13h
    jc    .GoOnReading        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止

    add    esp, 2
    pop    bp

    ret

;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;    找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax
;    需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
    push    es
    push    bx
    push    ax
    mov    ax, BaseOfKernelFile    ;
    sub    ax, 0100h        ; ┣ 在 BaseOfKernelFile 后面留出 4K 空间用于存放 FAT
    mov    es, ax            ;
    pop    ax
    mov    byte [bOdd], 0
    mov    bx, 3
    mul    bx            ; dx:ax = ax * 3
    mov    bx, 2
    div    bx            ; dx:ax / 2 ==> ax <-, dx <- 余数
    cmp    dx, 0
    jz    LABEL_EVEN
    mov    byte [bOdd], 1
LABEL_EVEN:;偶数
    xor    dx, dx            ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
    mov    bx, [BPB_BytsPerSec]
    div    bx            ; dx:ax / BPB_BytsPerSec ==>    ax <-(FATEntry 所在的扇区相对于 FAT 来说的扇区号)
                    ;                dx <- 余数 (FATEntry 在扇区内的偏移)
    push    dx
    mov    bx, 0            ; bx <- 0    于是, es:bx = (BaseOfKernelFile - 100):00 = (BaseOfKernelFile - 100) * 10h
    add    ax, SectorNoOfFAT1    ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
    mov    cl, 2
    call    ReadSector        ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
    pop    dx
    add    bx, dx
    mov    ax, [es:bx]
    cmp    byte [bOdd], 1
    jnz    LABEL_EVEN_2
    shr    ax, 4
LABEL_EVEN_2:
    and    ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

    pop    bx
    pop    es
    ret
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
; 函数名: KillMotor
;----------------------------------------------------------------------------
; 作用:
;    关闭软驱马达
KillMotor:
    push    dx
    mov    dx, 03F2h
    mov    al, 0
    out    dx, al
    pop    dx
    ret
;----------------------------------------------------------------------------

看没看到这个%include    "fat12hdr.inc",嘿嘿 把FAT12文件有关的内容放到一个文件去了。就是头文件了,inc就是include 。就是这些东西:


 



; FAT12 磁盘的头
; ----------------------------------------------------------------------
BS_OEMName    DB 'ForrestY'    ; OEM String, 必须 8 个字节

BPB_BytsPerSec    DW 512        ; 每扇区字节数
BPB_SecPerClus    DB 1        ; 每簇多少扇区
BPB_RsvdSecCnt    DW 1        ; Boot 记录占用多少扇区
BPB_NumFATs    DB 2        ; 共有多少 FAT 表
BPB_RootEntCnt    DW 224        ; 根目录文件数最大值
BPB_TotSec16    DW 2880        ; 逻辑扇区总数
BPB_Media    DB 0xF0        ; 媒体描述符
BPB_FATSz16    DW 9        ; 每FAT扇区数
BPB_SecPerTrk    DW 18        ; 每磁道扇区数
BPB_NumHeads    DW 2        ; 磁头数(面数)
BPB_HiddSec    DD 0        ; 隐藏扇区数
BPB_TotSec32    DD 0        ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数

BS_DrvNum    DB 0        ; 中断 13 的驱动器号
BS_Reserved1    DB 0        ; 未使用
BS_BootSig    DB 29h        ; 扩展引导标记 (29h)
BS_VolID    DD 0        ; 卷序列号
BS_VolLab    DB 'MyOS 0.01 '; 卷标, 必须 11 个字节
BS_FileSysType    DB 'FAT12 '    ; 文件系统类型, 必须 8个字节
;------------------------------------------------------------------------


; -------------------------------------------------------------------------
; 基于 FAT12 头的一些常量定义,如果头信息改变,下面的常量可能也要做相应改变
; -------------------------------------------------------------------------
FATSz            equ    9 ; BPB_FATSz16
RootDirSectors        equ    14 ; 根目录占用空间: RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec; 但如果按照此公式 ; 代码过长
SectorNoOfRootDirectory    equ    19 ; Root Directory 的第一个扇区号    = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)
SectorNoOfFAT1        equ    1 ; FAT1 的第一个扇区号    = BPB_RsvdSecCnt
DeltaSectorNo        equ    17 ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
                 ; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

好了编译下 nasm loader.asm -o loader.bin

下面就是MyOSboot.asm了,它就把刚才的头信息变成%include    "fat12hdr.inc",其余内容和第二阶段一样没有变。

nasm MyOSboot.asm -o MyOSboot.bin

下面就要写个kernel。asm了,为了看到加载是否成功,kernel先简单写了个显示功能:


 


; 编译链接方法
; (ld 的‘-s’选项意为“strip all”)
;
; [root@XXX XXX]# nasm -f elf kernel.asm -o kernel.o
; [root@XXX XXX]# ld -s kernel.o -o kernel.bin
; [root@XXX XXX]#

[section .text]    ; 代码在此

global _start    ; 导出 _start

_start:    ; 跳到这里来的时候,我们假设 gs 指向显存
    mov    ah, 0Fh                ; 0000: 黑底 1111: 白字
    mov    al, 'K'
    mov    [gs:((80 * 1 + 39) * 2)], ax    ; 屏幕第 1 行, 第 39 列。
    jmp    $

以为内核要ELF格式的 所以要用Linux下nasm来编译,俺安装了VMware和fedora,以前装的,所以现在就直接用了。

nasm -f elf -o kernel.o kernel.asm

ld -s -o kernel.bin kernel.o

好了 loader.bin  MyOSboot.bin  kernel.bin都有了,我们还是和以前一样生成映像,利用floppywriter把MyOSboot.bin写到起始扇区,利用winimage把loader.bin和kernel.bin写到映像MyOS.img.OK了。运行bochsrc.bxrc即可。

哎,工具都还没用熟呢。。。下阶段要跳入保护模式。。。。

阅读(870) | 评论(0) | 转发(0) |
0

上一篇:第二阶段 突破512字节

下一篇:32位寄存器

给主人留下些什么吧!~~