pmtest6.asm

这个代码的功能是写了一个分页机制,还有书中的一个bug
解决pmtest6.asm不能返回到实模式的问题

;======================================
;pmtest6.asm
;编译方法:nasm pmtest6.asm -o pmtest6.bin
;======================================
%include "pm.inc";  常量,宏 以及一些说明
;org 07c00h

PageDirBase equ 200000h;  页目录开始地址2M
PageTblBase equ 201000h;  页表开始地址 2M+4K

org 0100h
    jmp LABEL_BEGIN

[SECTION .gdt]
;GDT    段基址  段界限 属性

LABEL_GDT:  Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ;  NORMAL描述符

LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW; page directory 4k
LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;page tabe  4M


LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32;  非一致代码段
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C;  
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32;   
LABEL_DESC_VIDEO : Descriptor 0B8000h, 0ffffh, DA_DRW;  显存首地址
;GDT 就是一个数组结构

GdtLen  equ  $-LABEL_GDT  ;GDT长度
GdtPtr  dw   GdtLen - 1  ;GDT 界限
dd 0  ; GDT基地址
;GdtPtr也是一个数据结构  前2字节是GDT界限  后4字节是GDT基地址

;GDT 选择子
SelectorNormal  equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorPageDir equ LABEL_DESC_PAGE_DIR- LABEL_GDT
SelectorPageTbl equ LABEL_DESC_PAGE_TBL- LABEL_GDT

SelectorCode32  equ     LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16  equ     LABEL_DESC_CODE16 - LABEL_GDT
SelectorData    equ     LABEL_DESC_DATA - LABEL_GDT
SelectorStack   equ     LABEL_DESC_STACK - LABEL_GDT
SelectorVideo   equ LABEL_DESC_VIDEO - LABEL_GDT

; END of [SECTION .gdt]



[SECTION .data1]  ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode   dw  0;  字符串
PMMessage:  db "In Protect Mode now.^_^", 0;  在保护模式中显示
OffsetPMMessage equ PMMessage - $$
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]



;这是一个16位代码段 这个程序修改了gdt中的一些值 然后执行跳转到第三个section
[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     [SPValueInRealMode],sp

    ;初始化16位的代码段描述符
    xor eax, eax
    mov     ax, cs
    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

    ;初始化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

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

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;  开中断

    mov ax, 4c00h;
    int 21h;    回到dos
;end of section .s16




[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:
    call    SetupPaging

    mov ax, SelectorData
    mov ds, ax;         数据段选择子

    mov ax, SelectorVideo;  视频段选择子
    mov gs, ax

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

    mov esp, TopOfStack

    ;下面显示一个字符串
    mov ah, 0Ch;        0000黑底 1100红字
    xor esi, esi
    xor edi, edi
    mov esi, OffsetPMMessage;   源数据偏移
    mov edi, (80*10 + 10) * 2;  目的数据偏移  屏幕第10行第0列
    cld             ;告诉字符串向前移动

.1:
    lodsb       ;将字符串中的si指针所指向的一个字节装入al中
    test    al, al  ;判断al是否为空  为空跳转到2
    jz  .2
    mov [gs:edi], ax ;不为空显示当前字符
    add edi, 2    ;edi加二  
    jmp .1

.2: ;当第一条字符串显示完毕

    ;到此停止
    jmp SelectorCode16:0
;-------------------------------------------------------------------------
SetupPaging:
;  为简化处理  所有线性地址对应相等的物理地址
;首先初始化页目录
    mov ax, SelectorPageDir;此段首地址为pagedirbase
    mov es, ax
    mov ecx, 1024 ; 共1K个表项
    xor edi, edi
    xor eax, eax
    mov eax, PageTblBase|PG_P|PG_USU|PG_RWW
.1:
    stosd
    add eax, 4096;  为了简化 所有页表在内存中都是连续的
loop .1
    ;再初始化所有页表(1K个  4M内存空间)
    mov ax, SelectorPageTbl;  此段首地址为 pagetblbase
    mov es, ax
    mov ecx, 1024 * 1024;  共1M个页表项 也既有1M个页
    xor     edi, edi
    xor eax, eax
    mov eax, PG_P | PG_USU | PG_RWW   ;第一个页的地址
.2:
    stosd
    add eax, 4096;  每一页指向4K的空间
    loop .2

    mov eax, PageDirBase
    mov cr3, eax    ;cr3 指向页目录表
    mov eax, cr0
    or  eax, 80000000h
    mov cr0, eax   ;PG = 1  分页机制生效

jmp short .3
.3:
    nop
    ret
    ;

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
    and eax, 7ffffffeh
    mov cr0, eax

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

Code16Len equ $ - LABEL_SEG_CODE16

;end of section .s16code

猜你喜欢

转载自blog.csdn.net/u012323667/article/details/79400225
ASM
今日推荐