保护模式进阶

这个程序的核心目的是:试验大地址的读写,在保护模式下面寻址空间可达4GB,实模式下只能寻址1MB。(why:为什么保护模式下能有这么大的寻址能力,而实模式下不行?answer:实模式下有20位的地址总线,保护模式下有32位地址总线)

读写es段,5MB处的数据。首先读出开始处的8字节的内容,然后写入一个字符串,再从中读出。

读写字符串,使用的是[es:edi]和[es:esi]

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

; pmtest2.asm

; 编译方法:nasm pmtest2.asm -o pmtest2.com

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

%include "pm.inc" ; 常量, 宏, 以及一些说明

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_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32; 非一致代码段, 32

LABEL_DESC_CODE16: 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

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

; 字符串

PMMessage: db "In Protect Mode now. ^-^", 0 ; 在保护模式中显示

OffsetPMMessage equ PMMessage - $$

StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0

OffsetStrTest equ StrTest - $$

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   将LABEL_GO_BACK_TO_REAL中的jmp指令的第三个字节修改位cs_real_mode值,实现jmp正确的跳转

        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

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

        mov ax, 4c00h ; `.

        int 21h ; /  回到 DOS

 ; 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

        ; 下面显示一个字符串

        mov ah, 0Ch ; 0000: 黑底    1100: 红字

        xor esi, esi

        xor edi, edi

        mov esi, OffsetPMMessage ; 源数据偏移

        mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。

        cld  将数据从esi拷贝到edi

.1:

        lodsb

        test al, al 测试al是否为空

        jz .2

        mov [gs:edi], ax

        add edi, 2

        jmp .1

.2: ; 显示完毕

        call DispReturn

        call TestRead

        call TestWrite

        call TestRead

        ; 到此停止

        jmp SelectorCode16:0

; ------------------------------------------------------------------------

TestRead:     将[es:esi]中的数据逐个以16进制形式显示出来

        xor esi, esi

        mov ecx, 8

.loop:

        mov al, [es:esi]

        call DispAL

        inc esi

        loop .loop

        call DispReturn

        ret

; TestRead 结束-----------------------------------------------------------

; ------------------------------------------------------------------------

TestWrite:   OffsetStrTest的数据写入到[es:edi]

        push esi

        push edi

        xor esi, esi

        xor edi, edi

        mov esi, OffsetStrTest ; 源数据偏移

        cld

.1:

        lodsb

        test al, al 

        jz .2      测试寄存器al是否为空,如果为空,则跳转2

        mov [es:edi], al    

        inc edi

        jmp .1

.2:

        pop edi

        pop esi

        ret

; TestWrite 结束----------------------------------------------------------

; ------------------------------------------------------------------------

; 显示 AL 中的数字

; 默认地:

; 数字已经存在 AL 中

; edi 始终指向要显示的下一个字符的位置

; 被改变的寄存器:

; ax, edi

; ------------------------------------------------------------------------

DispAL: 将al中的字节用16进制形式显示出来

        push ecx

        push edx

        mov ah, 0Ch ; 0000: 黑底    1100: 红字

        mov dl, al

        shr al, 4

        mov ecx, 2

.begin:

        and al, 01111b

        cmp al, 9

        ja .1

        add al, '0'

        jmp .2

.1:

        sub al, 0Ah

        add al, 'A'

.2:

        mov [gs:edi], ax

        add edi, 2

        mov al, dl

        loop .begin

        add edi, 2

        pop edx

        pop ecx

        ret

; DispAL 结束-------------------------------------------------------------

; ------------------------------------------------------------------------

DispReturn: 修改的edi的值,让下一个字符显示在下一行的开头处

        push eax

        push ebx 

        mov eax, edi

        mov bl, 160

        div bl

        and eax, 0FFh

        inc eax

        mov bl, 160

        mul bl

        mov edi, eax

        pop ebx

        pop eax

         ret

; DispReturn 结束---------------------------------------------------------


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]



一、cld汇编指令

与cld相对应的指令是std,二者均是用来操作方向标志位DF(Direction Flag)。cld使DF 复位,即是让DF=0,std使DF置位,即DF=1.这两个指令用于串操作指令中。通过执行cld或std指令可以控制方向标志DF,决定内存地址是增大(DF=0,向高地址增加)还是减小(DF=1,向地地址减小)。

源操作数和目的操作数分别使用寄存器(e)si和(e)di进行间接寻址;每执行一次串操作,源指针(e)si和目的指针(e)di将自动进行修改:±1、±2、±4,其对应的分别是字节操作、字操作和双字操作。

在执行该指令之前,必须预置SI和DI的初值,用STD或CLD设置DF值.

MOVS DST , SRC //同上,不常用,DST和SRC只是用来用类型检查,并不允许使用其它寻址方式来确定操作数.

        1.目的串必须在附加段中,即必须是ES:[DI]

        2.源串允许使用段跨越前缀来修饰,但偏移地址必须是[SI].

二、lodsb 字符串操作指令

汇编语言中,串操作指令LODSB/LODSW是块装入指令,其具体操作是把SI指向的存储单元读入累加器,LODSB就读入AL,LODSW就读入AX中,然后SI自动增加或减小1或2.其常常是对数组或字符串中的元素逐个进行处理。

三、test汇编指令

test属于逻辑运算指令

功能: 执行BIT与BIT之间的逻辑运算

测试(两操作数作与运算,仅修改标志位,不回送结果).

Test对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。TEST AX,BX 与 AND AX,BX 命令有相同效果

语法: TEST r/m,r/m/data

影响标志: C,O,P,Z,S(其中C与O两个标志会被设为0)

保护模式进阶_第1张图片

四、div指令


保护模式进阶_第2张图片

五、loop指令

loop指令的格式是:loop标号,cpu执行loop指令的时候,要进行两步操作

1.(cx)=(cx)-1

2.判断cx中的值,不为零则转至标号处执行,如果为零,则向下执行

你可能感兴趣的:(保护模式进阶)