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

全部博文(164)

文章存档

2011年(1)

2010年(108)

2009年(55)

我的朋友

分类:

2010-06-21 09:45:19

哈哈哈 先乐一下 虽然我又懒又笨 
 
下面说说俺怎么滴突破512字节滴 当然了 还是从书上看来的再加上俺自己滴理解
 
书上说:一个操作系统从开机到开始运行,大致经历 引导--加载内核入内存--跳入保护模式--开始执行内核  这样一个过程。在内核开始执行之前不但要加载内核,而且还有准备保护模式等一系列工作,如果全部交给引导扇区来做,512字节很可能是不够用的,所以,不妨把这个过程交给另外模块来完成--loader。引导扇区负责把loader加载入内存并且把控制权交给它,其他工作交给loader来做,它没有512字节的限制。
为了实验简单,先来一个简单的loader:

org    0100h

    mov    ax, 0B800h
    mov    gs, ax
    mov    ah, 0Fh                ; 0000: 黑底 1111: 白字
    mov    al, 'L'
    mov    [gs:((80 * 0 + 39) * 2)], ax    ; 屏幕第 0 行, 第 39 列。

    jmp    $        ; Start

然后通过NASM编译:

nasm loader.asm -o loader.bin

然后就是boot代码了,这个书上讲的挺详细的。读懂就OK

MyOSboot.asm如下:


;%define    _BOOT_DEBUG_    ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试

%ifdef    _BOOT_DEBUG_
    org 0100h            ; 调试状态, 做成 .COM 文件, 可调试
%else
    org 07c00h            ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif

;================================================================================================
%ifdef    _BOOT_DEBUG_
BaseOfStack        equ    0100h    ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack        equ    07c00h    ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endif

BaseOfLoader        equ    09000h    ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader        equ    0100h    ; LOADER.BIN 被加载到的位置 ---- 偏移地址

RootDirSectors        equ    14    ; 根目录占用空间
SectorNoOfRootDirectory    equ    19    ; Root Directory 的第一个扇区号
SectorNoOfFAT1        equ    1    ; FAT1 的第一个扇区号    = BPB_RsvdSecCnt
DeltaSectorNo        equ    17    ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
                    ; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo
;================================================================================================

    jmp short LABEL_START        ; Start to boot.
    nop                ; 这个 nop 不可少

    ; 下面是 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 'Tinix0.01 '; 卷标, 必须 11 个字节
    BS_FileSysType    DB 'FAT12 '    ; 文件系统类型, 必须 8个字节

LABEL_START:    
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, BaseOfStack

    ; 清屏
    mov    ax, 0600h        ; AH = 6, AL = 0h
    mov    bx, 0700h        ; 黑底白字(BL = 07h)
    mov    cx, 0            ; 左上角: (0, 0)
    mov    dx, 0184fh        ; 右下角: (80, 50)
    int    10h            ; int 10h

    mov    dh, 0            ; "Booting "
    call    DispStr            ; 显示字符串
    
    xor    ah, ah    ;
    xor    dl, dl    ; ┣ 软驱复位
    int    13h    ;
    
; 下面在 A 盘的根目录寻找 LOADER.BIN
    mov    word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    cmp    word [wRootDirSizeForLoop], 0    ;
    jz    LABEL_NO_LOADERBIN        ; ┣ 判断根目录区是不是已经读完
    dec    word [wRootDirSizeForLoop]    ; ┛ 如果读完表示没有找到 LOADER.BIN
    mov    ax, BaseOfLoader
    mov    es, ax            ; es <- BaseOfLoader
    mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader
    mov    ax, [wSectorNo]    ; ax <- Root Directory 中的某 Sector 号
    mov    cl, 1
    call    ReadSector

    mov    si, LoaderFileName    ; ds:si -> "LOADER BIN"
    mov    di, OffsetOfLoader    ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
    cld
    mov    dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
    cmp    dx, 0                                        ; ┓循环次数控制,
    jz    LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    ; ┣如果已经读完了一个 Sector,
    dec    dx                                            ; ┛就跳到下一个 Sector
    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]
    jz    LABEL_GO_ON
    jmp    LABEL_DIFFERENT        ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:
    inc    di
    jmp    LABEL_CMP_FILENAME    ;    继续循环

LABEL_DIFFERENT:
    and    di, 0FFE0h                        ; else ┓    di &= E0 为了让它指向本条目开头
    add    di, 20h                            ;
    mov    si, LoaderFileName                    ;di += 20h 下一个目录条目
    jmp    LABEL_SEARCH_FOR_LOADERBIN;

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    add    word [wSectorNo], 1
    jmp    LABEL_SEARCH_IN_ROOT_DIR_BEGIN

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

LABEL_FILENAME_FOUND:            ; 找到 LOADER.BIN 后便来到这里继续
    mov    ax, RootDirSectors
    and    di, 0FFE0h        ; di -> 当前条目的开始
    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, BaseOfLoader
    mov    es, ax            ; es <- BaseOfLoader
    mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
    mov    ax, cx            ; ax <- Sector 号

LABEL_GOON_LOADING_FILE:
    push    ax            ;
    push    bx            ;
    mov    ah, 0Eh            ; ┃ 每读一个扇区就在 "Booting " 后面打一个点, 形成这样的效果:
    mov    al, '.'            ;
    mov    bl, 0Fh            ; ┃ Booting ......
    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:

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

; *****************************************************************************************************
    jmp    BaseOfLoader:OffsetOfLoader    ; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
                        ; 开始执行 LOADER.BIN 的代码
                        ; Boot Sector 的使命到此结束
; *****************************************************************************************************



;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop    dw    RootDirSectors    ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo        dw    0        ; 要读取的扇区号
bOdd            db    0        ; 奇数还是偶数

;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName        db    "LOADER BIN", 0    ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength        equ    9
BootMessage:        db    "Booting "; 9字节, 不够则用空格补齐. 序号 0
Message1        db    "Ready. "; 9字节, 不够则用空格补齐. 序号 1
Message2        db    "No LOADER"; 9字节, 不够则用空格补齐. 序号 2
;============================================================================


;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;    显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:
    mov    ax, MessageLength
    mul    dh
    add    ax, BootMessage
    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
    int    10h            ; int 10h
    ret


;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;    从第 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, BaseOfLoader    ;
    sub    ax, 0100h        ; ┣ 在 BaseOfLoader 后面留出 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 = (BaseOfLoader - 100):00 = (BaseOfLoader - 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
;----------------------------------------------------------------------------

times     510-($-$$)    db    0    ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw     0xaa55                ; 结束标志


nasn MyOSboot.asm  -o MyOSboot.bin
 
好了 文件已经齐了 ,下面我们就把MyOSboot.bin用floppywriter写到MyOS.img。然后下载一个winimage打开MyOS.img,把loader。bin写入MyOS.img。搞定!运行bochsrc.bxrc就成功了。
这次主要学到了新工具winimage哈哈,是网友介绍的。我承认我真的很笨,啥工具都不知道。。。。。
文件: MyOS2.rar
大小: 3360KB
下载: 下载
阅读(1116) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~