汇编网首页登录博客注册
shinefan的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  

[2012-08-30 12:43] 保护模式下的课程设计2

图片载入中
花了四天时间,总算把保护模式下的课程设计2给做好了。
共写了两个程序,第一个是主程序,需要事先保存在A盘(FAT12)下,存为LOADER.COM。第二个是引导安装程序,运行后会把引导程序写入A盘引导扇区。

PS:中间有的注释是错的,有时间改过来!

环境:NASM
参考了《自己动手写操作系统》。


;主程序

DA_32                EQU        4000h        ; 32 位段
DA_LIMIT_4K        EQU        8000h        ; 段界限粒度为 4K 字节

DA_DPL0                EQU          00h        ; DPL = 0
DA_DPL1                EQU          20h        ; DPL = 1
DA_DPL2                EQU          40h        ; DPL = 2
DA_DPL3                EQU          60h        ; DPL = 3
;----------------------------------------------------------------------------
; 存储段描述符类型值说明
;----------------------------------------------------------------------------
DA_DR                EQU        90h        ; 存在的只读数据段类型值
DA_DRW                EQU        92h        ; 存在的可读写数据段属性值
DA_DRWA                EQU        93h        ; 存在的已访问可读写数据段类型值
DA_C                EQU        98h        ; 存在的只执行代码段属性值
DA_CR                EQU        9Ah        ; 存在的可执行可读代码段属性值
DA_CCO                EQU        9Ch        ; 存在的只执行一致代码段属性值
DA_CCOR                EQU        9Eh        ; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
; 系统段描述符类型值说明
;----------------------------------------------------------------------------
DA_LDT                EQU          82h        ; 局部描述符表段类型值
DA_TaskGate        EQU          85h        ; 任务门类型值
DA_386TSS        EQU          89h        ; 可用 386 任务状态段类型值
DA_386CGate        EQU          8Ch        ; 386 调用门类型值
DA_386IGate        EQU          8Eh        ; 386 中断门类型值
DA_386TGate        EQU          8Fh        ; 386 陷阱门类型值
;----------------------------------------------------------------------------


; 选择子图示:
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9  ┃ 8  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
;         ┃                                 描述符索引                                 ┃ TI ┃   RPL    ┃
;         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
;
; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
;        TI=0 指示从全局描述符表GDT中读取描述符;
;        TI=1 指示从局部描述符表LDT中读取描述符。
;

;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:
;       SA_  : Selector Attribute

SA_RPL0                EQU        0        ; ┓
SA_RPL1                EQU        1        ; ┣ RPL
SA_RPL2                EQU        2        ; ┃
SA_RPL3                EQU        3        ; ┛

SA_TIG                EQU        0        ; ┓TI
SA_TIL                EQU        4        ; ┛
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P                EQU        1        ; 页存在属性位
PG_RWR                EQU        0        ; R/W 属性位值, 读/执行
PG_RWW                EQU        2        ; R/W 属性位值, 读/写/执行
PG_USS                EQU        0        ; U/S 属性位值, 系统级
PG_USU                EQU        4        ; U/S 属性位值, 用户级
;----------------------------------------------------------------------------




; =========================================
; FLAGS - Intel 8086 Family Flags Register
; =========================================
;
;      |11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
;        |  | | | | | | | | | | | | | | | | '---  CF……Carry Flag
;        |  | | | | | | | | | | | | | | | '---  1
;        |  | | | | | | | | | | | | | | '---  PF……Parity Flag
;        |  | | | | | | | | | | | | | '---  0
;        |  | | | | | | | | | | | | '---  AF……Auxiliary Flag
;        |  | | | | | | | | | | | '---  0
;        |  | | | | | | | | | | '---  ZF……Zero Flag
;        |  | | | | | | | | | '---  SF……Sign Flag
;        |  | | | | | | | | '---  TF……Trap Flag  (Single Step)
;        |  | | | | | | | '---  IF……Interrupt Flag
;        |  | | | | | | '---  DF……Direction Flag
;        |  | | | | | '---  OF……Overflow flag
;        |  | | | '-----  IOPL……I/O Privilege Level  (286+ only)
;        |  | | '-----  NT……Nested Task Flag  (286+ only)
;        |  | '-----  0
;        |  '-----  RF……Resume Flag (386+ only)
;        '------  VM……Virtual Mode Flag (386+ only)
;
;        注: see   PUSHF  POPF  STI  CLI  STD  CLD
;


; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
        dw        %2 & 0FFFFh                                ; 段界限 1                                (2 字节)
        dw        %1 & 0FFFFh                                ; 段基址 1                                (2 字节)
        db        (%1 >> 16) & 0FFh                        ; 段基址 2                                (1 字节)
        dw        ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)        ; 属性 1 + 段界限 2 + 属性 2                (2 字节)
        db        (%1 >> 24) & 0FFh                        ; 段基址 3                                (1 字节)
%endmacro ; 共 8 字节
;
; 门
; usage: Gate Selector, Offset, DCount, Attr
;        Selector:  dw
;        Offset:    dd
;        DCount:    db
;        Attr:      db
%macro Gate 4
        dw        (%2 & 0FFFFh)                                ; 偏移 1                                (2 字节)
        dw        %1                                        ; 选择子                                (2 字节)
        dw        (%3 & 1Fh) | ((%4 << 8) & 0FF00h)        ; 属性                                        (2 字节)
        dw        ((%2 >> 16) & 0FFFFh)                        ; 偏移 2                                (2 字节)
%endmacro ; 共 8 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
org        0100h
        jmp        LABEL_BEGIN

%macro cls 0
        mov        al,0
        mov        ecx,10000h
        mov        bx,0
%%l:
        mov        [gs:bx],al
        xor        al,7
        inc        bx
        loop        %%l
%endmacro

%macro settime 2
        mov        ah,[%1]
        mov        cl,4
        shl        ah,cl
        add        ah,[%1+1]
        mov        al,%2
        out        70h,al
        mov        al,ah
        out        71h,al
%endmacro

%macro skipmark 0
        cmp        si,2
        jz        %%skip
        cmp        si,4
        jz        %%skip
        cmp        si,6
        jz        %%skip
        cmp        si,8
        jz        %%skip
        cmp        si,10
        jz        %%skip
        jmp        %%ctn
%%skip:
        add        edi,2
%%ctn:
%endmacro

%macro skipmark_d 0
        cmp        si,1
        jz        %%skip
        cmp        si,3
        jz        %%skip
        cmp        si,5
        jz        %%skip
        cmp        si,7
        jz        %%skip
        cmp        si,9
        jz        %%skip
        jmp        %%ctn
%%skip:
        sub        edi,2
%%ctn:
%endmacro

%macro slt 2
        mov        bx, 0b800h
        mov        ds,bx
        mov        byte        [(80 * 15 + %2) * 2],%1
%endmacro


[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:                Descriptor               0,                 0, 0                     ; 空描述符
LABEL_DESC_NORMAL:        Descriptor               0,            0ffffh, DA_DRW                ; Normal 描述符
LABEL_DESC_CODE32:        Descriptor               0,  SegCode32Len - 1, DA_C + DA_32        ; 非一致代码段, 32
LABEL_DESC_CODE16:        Descriptor               0,            0ffffh, DA_C                ; 非一致代码段, 16
LABEL_DESC_CODE16_2:        Descriptor               0,            0ffffh, DA_C                ; 非一致代码段, 16
LABEL_DESC_DATA:        Descriptor               0,        DataLen - 1, DA_DRW                ; Data
LABEL_DESC_STACK:        Descriptor               0,        TopOfStack, DA_DRWA + DA_32        ; Stack, 32 位
LABEL_DESC_TEST:        Descriptor        0500000h,            0ffffh, DA_DRW
LABEL_DESC_VIDEO:        Descriptor         0B8000h,            0ffffh, DA_DRW                ; 显存首地址
; GDT 结束

GdtLen                equ        $ - LABEL_GDT        ; GDT长度
GdtPtr                dw        GdtLen - 1        ; GDT界限
                dd        0                ; GDT基地址

; GDT 选择子
SelectorNormal                equ        LABEL_DESC_NORMAL        - LABEL_GDT
SelectorCode32                equ        LABEL_DESC_CODE32        - LABEL_GDT
SelectorCode16                equ        LABEL_DESC_CODE16        - LABEL_GDT
SelectorCode16_2        equ        LABEL_DESC_CODE16_2        - LABEL_GDT
SelectorData                equ        LABEL_DESC_DATA                - LABEL_GDT
SelectorStack                equ        LABEL_DESC_STACK        - LABEL_GDT
SelectorTest                equ        LABEL_DESC_TEST                - LABEL_GDT
SelectorVideo                equ        LABEL_DESC_VIDEO        - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]         ; 数据段
ALIGN        32
[BITS        32]
LABEL_DATA:
SPValueInRealMode        dw        0
; 字符串
m1:                db        "1)Reset PC", 0
m2:                db        "2)Start system",0
m3:                db        "3)Clock",0
m4:                db        "4)Set clock",0
o1                equ        m1 - $$
o2                equ        m2 - $$
o3                equ        m3 - $$
o4                equ        m4 - $$
tm:                db        "Enter new time(yy/mm/dd hh:mm:ss): "
ot                equ        tm - $$
stl                equ        $ - tm
tmc:                db        "  /  /     :  :",0
td:                times        12        db        0
od                equ        td - $$
menu                db        0
om                equ        menu-$$
DataLen                equ        $ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN        32
[BITS        32]
LABEL_STACK:
        times 512 db 0

TopOfStack        equ        $ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS        16]
LABEL_BEGIN:
        mov        ax, cs
        mov        ds, ax
        mov        es, ax
        mov        ss, ax
        mov        sp, 0100h

        mov        [LABEL_GO_BACK_TO_REAL+3], ax
        mov        [LABEL_GO_BACK_TO_REAL_2+3], ax
        mov        [SPValueInRealMode], sp

        ; 初始化 16 位代码段描述符
        mov        ax, cs
        movzx        eax, ax
        shl        eax, 4
        add        eax, LABEL_SEG_CODE16
        mov        word [LABEL_DESC_CODE16 + 2], ax
        shr        eax, 16
        mov        byte [LABEL_DESC_CODE16 + 4], al
        mov        byte [LABEL_DESC_CODE16 + 7], ah

        mov        ax, cs
        movzx        eax, ax
        shl        eax, 4
        add        eax, LABEL_SEG_CODE16_2
        mov        word [LABEL_DESC_CODE16_2 + 2], ax
        shr        eax, 16
        mov        byte [LABEL_DESC_CODE16_2 + 4], al
        mov        byte [LABEL_DESC_CODE16_2 + 7], ah

        ; 初始化 32 位代码段描述符
        xor        eax, eax
        mov        ax, cs
        shl        eax, 4
        add        eax, LABEL_SEG_CODE32
        mov        word [LABEL_DESC_CODE32 + 2], ax
        shr        eax, 16
        mov        byte [LABEL_DESC_CODE32 + 4], al
        mov        byte [LABEL_DESC_CODE32 + 7], ah

        ; 初始化数据段描述符
        xor        eax, eax
        mov        ax, ds
        shl        eax, 4
        add        eax, LABEL_DATA
        mov        word [LABEL_DESC_DATA + 2], ax
        shr        eax, 16
        mov        byte [LABEL_DESC_DATA + 4], al
        mov        byte [LABEL_DESC_DATA + 7], ah

        ; 初始化堆栈段描述符
        xor        eax, eax
        mov        ax, ds
        shl        eax, 4
        add        eax, LABEL_STACK
        mov        word [LABEL_DESC_STACK + 2], ax
        shr        eax, 16
        mov        byte [LABEL_DESC_STACK + 4], al
        mov        byte [LABEL_DESC_STACK + 7], ah

        ; 为加载 GDTR 作准备
        xor        eax, eax
        mov        ax, ds
        shl        eax, 4
        add        eax, LABEL_GDT                ; eax <- gdt 基地址
        mov        dword [GdtPtr + 2], eax        ; [GdtPtr + 2] <- gdt 基地址

        ; 加载 GDTR
        lgdt        [GdtPtr]

        ; 关中断
        cli

        ; 打开地址线A20
        in        al, 92h
        or        al, 00000010b
        out        92h, al

        ; 准备切换到保护模式
        mov        eax, cr0
        or        eax, 1
        mov        cr0, eax

        ; 真正进入保护模式
        jmp        dword SelectorCode32:0        ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:                ; 从保护模式跳回到实模式就到了这里
        mov        ax, cs
        mov        ds, ax
        mov        es, ax
        mov        ss, ax

        mov        sp, [SPValueInRealMode]

        in        al, 92h                ; ┓
        and        al, 11111101b        ; ┣ 关闭 A20 地址线
        out        92h, al                ; ┛

        sti                        ; 开中断

        db        0eah,0,0,0ffh,0ffh


LABEL_REAL_ENTRY_2:
        mov        ax, cs
        mov        ds, ax
        mov        es, ax
        mov        ss, ax

        mov        sp, [SPValueInRealMode]

        in        al, 92h                ; ┓
        and        al, 11111101b        ; ┣ 关闭 A20 地址线
        out        92h, al                ; ┛

        sti                        ; 开中断

        mov        al,1
        mov        ch,0
        mov        cl,1
        mov        dh,0
        mov        dl,80h
        mov        bx,0
        mov        es,bx
        mov        bx,7c00h
        mov        ah,2
        int        13h
        mov        ax,[es:7c00h+510d]
        cmp        ax,0aa55h
        jnz        error
        db        0eah,0,7ch,0,0

error:
        slt        'S',1
        slt        'y',2
        slt        's',3
        slt        't',4
        slt        'e',5
        slt        'm',6
        slt        ' ',7
        slt        'e',8
        slt        'r',9
        slt        'r',10
        slt        'o',11
        slt        'r',12
        slt        '.',13

        jmp        $

; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS        32]

LABEL_SEG_CODE32:
        mov        ax, SelectorData
        mov        ds, ax                        ; 数据段选择子
        mov        ax, SelectorTest
        mov        es, ax                        ; 测试段选择子
        mov        ax, SelectorVideo
        mov        gs, ax                        ; 视频段选择子

        mov        ax, SelectorStack
        mov        ss, ax                        ; 堆栈段选择子

        mov        esp, TopOfStack

ls:
        mov        ah,7
        mov        al,0
        mov        [gs:(80 * 10 + 18)*2],ax
        mov        [gs:(80 * 11 + 18)*2],ax
        mov        [gs:(80 * 12 + 18)*2],ax
        mov        [gs:(80 * 13 + 18)*2],ax

        mov        bl,[om]
        mov        bh,0
        mov        dx,bx
        mov        ecx,159
timesl:
        add        bx,dx
        loop        timesl

        mov        ah,7
        mov        al,'>'
        mov        [gs:bx+(80 * 10 + 18) * 2],ax

        ; 下面显示一个字符串
        mov        ah, 7
        xor        esi, esi
        xor        edi, edi
        mov        esi, o1        ; 源数据偏移
        mov        edi, (80 * 10 + 20) * 2        ; 目的数据偏移。屏幕第 10 行, 第 0 列。
        cld
l1:
        lodsb
        test        al, al
        jz        l2
        mov        [gs:edi], ax
        add        edi, 2
        jmp        l1

l2:
        mov        ah, 7                        ; 0000: 黑底    1100: 红字
        xor        esi, esi
        xor        edi, edi
        mov        esi, o2        ; 源数据偏移
        mov        edi, (80 * 11 + 20) * 2        ; 目的数据偏移。屏幕第 10 行, 第 0 列。
        cld
l3:
        lodsb
        test        al, al
        jz        l4
        mov        [gs:edi], ax
        add        edi, 2
        jmp        l3

l4:

        mov        ah, 7                        ; 0000: 黑底    1100: 红字
        xor        esi, esi
        xor        edi, edi
        mov        esi, o3        ; 源数据偏移
        mov        edi, (80 * 12 + 20) * 2        ; 目的数据偏移。屏幕第 10 行, 第 0 列。
        cld
l5:
        lodsb
        test        al, al
        jz        l6
        mov        [gs:edi], ax
        add        edi, 2
        jmp        l5

l6:
        mov        ah, 7                        ; 0000: 黑底    1100: 红字
        xor        esi, esi
        xor        edi, edi
        mov        esi, o4        ; 源数据偏移
        mov        edi, (80 * 13 + 20) * 2        ; 目的数据偏移。屏幕第 10 行, 第 0 列。
        cld
l7:
        lodsb
        test        al, al
        jz        l8
        mov        [gs:edi], ax
        add        edi, 2
        jmp        l7

l8:
        in        al,64h
        test        al,1
        jz        l8

        in        al,60h

        cmp        al,48h
        jz        up

        cmp        al,50h
        jz        down

        cmp        al,1ch
        jz        enter

        cmp        al,02

        jnz        l9
sub1:        jmp        SelectorCode16:0
l9:
        cmp        al,03
        jnz        l10
sub2:        jmp        SelectorCode16_2:0

l10: 
        cmp        al,04
        jnz        l11;g1

sub3:        cls

clock:
        mov        al, 0
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

        add        bl,30h
        add        al,30h

        mov        [gs:((80 * 0 + 69) * 2)], bl
        mov        [gs:((80 * 0 + 70) * 2)], al
;        jmp        ctn6

;l8g3:        jmp        l8

;ctn6:

;        jmp        ct3
;cg3:        jmp        clock
;ct3:
        mov        al, 2
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

;        jmp        ctn1

;l11g1:        jmp        l11g2

;ctn1:

        add        bl,30h
        add        al,30h

        mov        [gs:((80 * 0 + 66) * 2)], bl
        mov        [gs:((80 * 0 + 67) * 2)], al

        mov        al, 4
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

;        jmp        ct2
;cg2:        jmp        cg3
;ct2:
;        jmp        ctn5
;l8g2:        jmp        l8g3
;ctn5:

        add        bl,30h
        add        al,30h

        mov        [gs:((80 * 0 + 63) * 2)], bl
        mov        [gs:((80 * 0 + 64) * 2)], al


        mov        al,":"
        mov        [gs:((80 * 0 + 65) * 2)], al
        mov        [gs:((80 * 0 + 68) * 2)], al

        mov        al,"/"
        mov        [gs:((80 * 0 + 56) * 2)], al
        mov        [gs:((80 * 0 + 59) * 2)], al

;        jmp        ctn2

;l11g2:        jmp        l11g3

;ctn2:
        mov        al, 7
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

        add        bl,30h
        add        al,30h

        mov        [gs:((80 * 0 + 60) * 2)], bl
        mov        [gs:((80 * 0 + 61) * 2)], al

;        jmp        ct1
;cg1:        jmp        cg2
;ct1:

        mov        al, 8
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

        add        bl,30h
        add        al,30h

;        jmp        ctn4
;l8g1:        jmp        l8g2
;ctn4:
        mov        [gs:((80 * 0 + 57) * 2)], bl
        mov        [gs:((80 * 0 + 58) * 2)], al

;        jmp        ctn3
;l11g3:        jmp        l11
;ctn3:
        mov        al, 9
        out        70h,al
        in        al,71h
        mov        bl,al
        shr        bl,4
        and        al,1111b

        add        bl,30h
        add        al,30h

        mov        [gs:((80 * 0 + 54) * 2)], bl
        mov        [gs:((80 * 0 + 55) * 2)], al

        in        al,60h
        cmp        al,01
        jz        continue
        cmp        al,3bh
        jnz        clock

        mov        ecx,8000h
        mov        bx,1
        mov        al,1
loo:
        add        [gs:bx],al
        add        bx,2
        loop        loo

        jmp        clock;g1
continue:
        cls
        jmp        ls

l11:        cmp        al,5
        jnz        l13

sub4:        cls

        mov        ah, 7
        xor        esi, esi
        xor        edi, edi
        mov        esi, ot        ; 源数据偏移
        mov        edi, (80 * 10 + 0) * 2        ; 目的数据偏移。屏幕第 10 行, 第 0 列。
        cld
l:
        lodsb
        test        al, al
        jz        l12
        mov        [gs:edi], ax
        add        edi, 2
        jmp        l

l12:        mov        edi, (80 * 10 + stl) * 2
        mov        ah,11111111b
        mov        [gs:edi+1],ah
        mov        si,0

setclk:
wait_:        in        al,64h
        test        al,1
        jz        wait_


        in        al,60h
        cmp        al,01
        jz        l13_c

        cmp        al,4bh
        jz        left

        cmp        al,4dh
        jz        right

        cmp        al,0eh
        jz        del

        cmp        al,53h
        jz        clear

        cmp        al,1ch
        jz        set

        cmp        al,2
        jb        setclk

        cmp        si,11
        ja        setclk

        cmp        al,0bh
        ja        setclk
        jz        zero

        sub        al,1
        mov        [od+si],al
        add        al,48
        mov        [gs:edi],al
        mov        ah,[gs:edi+1]
        and        ah,00000111b
        mov        [gs:edi+1],ah
        inc        si
        add        edi,2
        skipmark
        mov        ah,[gs:edi+1]
        or        ah,11111000b
        mov        [gs:edi+1],ah
        jmp        setclk

zero:
        mov        al,0
        mov        [od+si],al
        add        al,48
        mov        [gs:edi],al
        mov        ah,[gs:edi+1]
        and        ah,00000111b
        mov        [gs:edi+1],ah
        inc        si
        add        edi,2
        skipmark
        mov        ah,[gs:edi+1]
        or        ah,11111000b
        mov        [gs:edi+1],ah
        jmp        setclk

set:        settime        od,9
        settime        od+2,8
        settime        od+4,7
        settime        od+6,4
        settime        od+8,2
        settime        od+10,0

l13_c:
        cls

l13:
        jmp        ls

del:
        cmp        si,0
        jz        setclk
        mov        ah,0
        dec        si
        mov        al,[gs:edi+1]
        and        al,00000111b
        mov        [gs:edi+1],al
        mov        [od+si],ah
        sub        edi,2
        skipmark_d
        mov        [gs:edi],ah
        mov        ah,[gs:edi+1]
        or        ah,11111000b
        mov        [gs:edi+1],ah
        jmp        setclk

left:
        cmp        si,0
        jz        setclk
        dec        si
        mov        ah,[gs:edi+1]
        and        ah,00000111b
        mov        [gs:edi+1],ah
        sub        edi,2
        skipmark_d
        mov        ah,[gs:edi+1]
        or        ah,11111000b
        mov        [gs:edi+1],ah
        jmp        setclk

right:
        cmp        si,11
        ja        setclk
        inc        si
        mov        ah,[gs:edi+1]
        and        ah,00000111b
        mov        [gs:edi+1],ah
        add        edi,2
        skipmark
        mov        ah,[gs:edi+1]
        or        ah,11111000b
        mov        [gs:edi+1],ah
        jmp        setclk

clear:
        cmp        si,11
        ja        setclk
        mov        ah,0
        mov        [od+si],ah
        mov        [gs:edi],ah
        jmp        setclk

up:
        mov        al,[om]
        dec        al
        cmp        al,11111111b
        jz        ovfl
upret:
        mov        [om],al
        jmp        ls

down:
        mov        al,[om]
        inc        al
        and        al,11b
        mov        [om],al
        jmp        ls

ovfl:
        mov        al,3
        jmp        upret

enter:
        mov        al,[om]
        cmp        al,0
        jz        sub1
        cmp        al,1
        jz        sub2
        cmp        al,2
        jz        sub3
        cmp        al,3
        jz        sub4
        jmp        ls

SegCode32Len        equ        $ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN        32
[BITS        16]
LABEL_SEG_CODE16:
        ; 跳回实模式:
        mov        ax, SelectorNormal
        mov        ds, ax
        mov        es, ax
        mov        fs, ax
        mov        gs, ax
        mov        ss, ax

        mov        eax, cr0
        and        al, 11111110b
        mov        cr0, eax

LABEL_GO_BACK_TO_REAL:
        jmp        0:LABEL_REAL_ENTRY        ; 段地址会在程序开始处被设置成正确的值

Code16Len        equ        $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

[SECTION .s16code_2]
ALIGN        32
[BITS        16]
LABEL_SEG_CODE16_2:
        ; 跳回实模式:
        mov        ax, SelectorNormal
        mov        ds, ax
        mov        es, ax
        mov        fs, ax
        mov        gs, ax
        mov        ss, ax

        mov        eax, cr0
        and        al, 11111110b
        mov        cr0, eax

LABEL_GO_BACK_TO_REAL_2:
        jmp        0:LABEL_REAL_ENTRY_2        ; 段地址会在程序开始处被设置成正确的值

Code16Len_2        equ        $ - LABEL_SEG_CODE16_2

; END of [SECTION .s16code]



;引导程序



        org        100h
;================================================================================================
%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
;================================================================================================

        mov        al,1
        mov        ch,0
        mov        cl,1
        mov        dx,0
        mov        bx,cs
        mov        es,bx
        mov        bx,main
        mov        ah,3
        int        13h

        mov        ax,4c00h
        int        21h

        times        07c00h-($-$$)-100h        db        0
main:
        jmp short LABEL_START                ; Start to boot.
        nop                                ; 这个 nop 不可少

        ; 下面是 FAT12 磁盘的头
        BS_OEMName        DB 'ShineFan'        ; 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个字节  

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                        ; 显示字符串

        mov        bh,0
        mov        dx,0
        mov        ah,2
        int        10h

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



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

;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName                db        "LOADER  COM", 0        ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength                equ        17
BootMessage:                db        "Loading system   "; 9字节, 不够则用空格补齐. 序号 0
Message1                db        "Done.            "; 9字节, 不够则用空格补齐. 序号 1
Message2                db        "System not found."; 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
评论次数(1)  |  浏览次数(664)  |  类型(汇编作业) |  收藏此文  | 

[  fpamc   发表于  2012-08-31 07:25  ]

顶顶,以后看 。。

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码