操作系统实践(7)——分页机制

为什么分页,分页有什么好处?

从苦逼码农的角度,用一个租房的例子来说,原来分段机制,就好比房子整租,当租客不想租的时候,就整个房子退还给房东,房东再找其它人整租,这样有个缺点,在北京,整租一套房子,很贵,而且很多租客都是一个人或者两个人,用不着租那么大,于是一方面房东如果找不到租客,房子就空闲着,一方面要是租客咬咬牙租下整套房子,很多房间也是很浪费。于是,下面要讨论的分页机制,就是现在市场上各种单间出租了。单间出租的好处很明显,房子利用率很高,一个租客不租了,很快就能找到下一个租客。其实分页的好处还体现在:对于寻址方式的影响。

分页与寻址方式

举个简单的例子,相信一开始使用u盘的时候体会过,一开始我们新建一个文件夹,然后把所有文件都往里面丢,当文件慢慢增多的时候,回头要找里面某个文件的时候,发现特别难找。于是,我们就会在顶层目录下,新建几个分类的文件夹,把不同的文件放到不同的类型的文件夹下,找的时候先找文件夹,再找文件。
细心的你一定会想,虽然放不同文件夹,但是打开几个文件夹也需要点几下鼠标,也是需要时间的,是的,cpu的分页机制的实现,在寻址的时候,多了几层间接寻址,虽然提高了灵活性,但是中间必然有损耗,从内存读入寄存器是需要花时间的,而且还有缓存失效的问题,所以,这一步就需要硬件支持了,根据我目前了解,cpu在这一层会做一些缓存处理,加快转换,减少中间间接寻址的损耗。

分页机制的实现

首先看图:
页表存储01
页表存储02

(1)根据cr3寄存器,找到页目录表在物理内存的位置;
(2)根据线性地址的高10位,在页目录表中,找到相应的页表地址。
(3)根据页表地址,找到页表。再根据中间10位,找到对应的页表中的一项。
(4)一个页表,映射的物理空间大小为4M (2^12 * 2^10项),所以所以每个页表的第一项映射到物理地址的4M*n,页表中的第q项,则映射到4M*n+4k*q,最后根据线性地址的低12位,找到最终的物理地址。


《orange’s一个操作系统的实现》里面,有一节专门写了个程序,让读者体验分页机制带来的好处。作者的思路:
1.拷贝两份代码到内存的不同地址,分别叫Foo和Bar。
2.初始化一个页目录和页表,访问一个线性地址(这里叫LinearAddr吧),这个线性地址跳转到Foo的代码执行,屏幕打印出Foo。
3.初始化另外一个页目录和页表,并修改LinearAddr这个线性地址对应的页表项,使访问LinearAddr的时候,会跳转到Bar的代码执行,屏幕打印出Bar。

体现出来的效果:同样访问的都是LinearAddr这个线性地址,切换页表后执行的是不同的代码。
这里非常关键的一点,就是修改页表项映射到的内存区域。

我这里举个不是特别恰当的例子来展示这种思想,用高级语言java举个例子:

interface Animal{
void duck();
}
class Dog implements Animal{
    @Override
    void duck(){
        println("wang wang~");
    }
}
class Cat implements Animal{
    @Override
    void duck(){
        println("miao miao~");
    }
}
Animal animal = new Dog();
animal.duck();//"wang wang~"
animal = new Cat();
animal.duck();//"miao miao~"

下面是用晦涩的汇编实现书中具的例子,做了简单的修改,修改的原因在于,我用书中的代码编译通过了,但是执行的时候bochs重启了,出错的代码在这两句

    mov ax,cs
    mov ds,ax

这两句看起来没什么问题,但实际上,我的实验结果,在32位保护模式下,把选择子赋值给ds,貌似cpu有寄存器的保护机制,如果是把代码段的选择子赋值给ds,则会出错,如果用数据段的选择子赋值给ds则没问题。如果读者有更好的见解,请留言,谢谢。

下面是实践结果和代码:

分页03

org 07c00h
    [BITS 16]

START:
    mov ax,cs
    mov ds,ax
    mov es,ax

    ;拷贝软盘中的代码到内存区
COPY:
    mov bx, COPY_CODE_START ;07c00h + 512(0100h) == 07e00h
    mov dl,0        ;驱动器号,软驱从0开始:0:软驱A,1:软驱B
                    ;磁盘从80h开始,80h:C盘,81h:D盘
    mov dh,0        ;磁头号,对于软盘即面号,一个面用一个磁头来读写
    mov ch,0        ;磁道号
    mov cl,2        ;扇区号
    mov al,0x0a     ;读取的扇区数
    mov ah,2        ;13h的功能号(2表示读扇区),es:bx指向
                    ;接收从扇区读入数据的内存区
    int 13h
    jc COPY         ;读取失败,CF表示为1,重试读取   

    jmp LABEL_BEGIN ;把程序读到内存区后,跳转到新的执行点

    ;补全512字节
    times 510-($-$$) db 0
    dw 0xaa55

COPY_CODE_START:

%include "inc.asm"


PageDirBase0        equ 200000h ; 页目录开始地址:  2M
PageTblBase0        equ 201000h ; 页表开始地址:       2M +  4K
PageDirBase1        equ 210000h ; 页目录开始地址:  2M + 64K
PageTblBase1        equ 211000h ; 页表开始地址:       2M + 64K + 4K

LinearAddrDemo  equ 00401000h
ProcFoo     equ 00401000h
ProcBar     equ 00501000h
ProcPagingDemo  equ 00301000h

[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_CODE_DEST: Descriptor 0,SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32
LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW+DA_DPL1    ; Data
LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位
LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW+DA_DPL3    ; 显存首地址

LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_STACK3:     Descriptor 0,       TopOfStack3, DA_DRWA+DA_32+DA_DPL3
LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS      ;TSS



; 门                               目标选择子,偏移,DCount, 属性
;LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,   0,     0, DA_386CGate+DA_DPL0
LABEL_CALL_GATE_TEST:   Gate          SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3

;分页
LABEL_DESC_FLAT_C:  Descriptor 0,        0fffffh, DA_CR|DA_32|DA_LIMIT_4K; 0~4G
LABEL_DESC_FLAT_RW: Descriptor 0,        0fffffh, DA_DRW|DA_LIMIT_4K     ; 0~4G

LABEL_DESC_CODE_DATA:   Descriptor    0,      CodeDataLen-1, 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
SelectorCodeDest    equ LABEL_DESC_CODE_DEST    - LABEL_GDT
SelectorData        equ LABEL_DESC_DATA     - LABEL_GDT
SelectorStack       equ LABEL_DESC_STACK    - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
SelectorCallGateTest    equ LABEL_CALL_GATE_TEST    - LABEL_GDT

SelectorCodeRing3   equ LABEL_DESC_CODE_RING3   - LABEL_GDT + SA_RPL3
SelectorStack3      equ LABEL_DESC_STACK3   - LABEL_GDT + SA_RPL3
SelectorTSS     equ LABEL_DESC_TSS      - LABEL_GDT

SelectorFlatC       equ LABEL_DESC_FLAT_C   - LABEL_GDT
SelectorFlatRW      equ LABEL_DESC_FLAT_RW  - LABEL_GDT
SelectorCodeData    equ LABEL_DESC_CODE_DATA    - LABEL_GDT

; END of [SECTION .gdt]

[SECTION .data1]     ; 数据段
ALIGN   32
[BITS   32]
LABEL_DATA:
; 字符串
PMMessage:      db  "In Protect Mode now. ^-^", 0   ; 在保护模式中显示
OffsetPMMessage     equ PMMessage - $$

_szPMMessage:           db  "In Protect Mode now. ^-^ =_= - -", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串
_szMemChkTitle:         db  "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0   ; 进入保护模式后显示此字符串
_szRAMSize          db  "RAM size:", 0
_szReturn           db  0Ah, 0
; 变量
_wSPValueInRealMode     dw  0
_dwMCRNumber:           dd  0   ; Memory Check Result
_dwDispPos:         dd  (80 * 6 + 0) * 2    ; 屏幕第 6 行, 第 0 列。
_dwMemSize:         dd  0
_ARDStruct:         ; Address Range Descriptor Structure
    _dwBaseAddrLow:     dd  0
    _dwBaseAddrHigh:    dd  0
    _dwLengthLow:       dd  0
    _dwLengthHigh:      dd  0
    _dwType:        dd  0
_PageTableNumber        dd  0

_MemChkBuf: times   256 db  0

; 保护模式下使用这些符号
szPMMessage     equ _szPMMessage    - $$
szMemChkTitle		equ	_szMemChkTitle	- $$
szRAMSize       equ _szRAMSize  - $$
szReturn		equ	_szReturn	- $$
dwDispPos       equ _dwDispPos  - $$
dwMemSize		equ	_dwMemSize	- $$
dwMCRNumber     equ _dwMCRNumber    - $$;多少块内存描述
ARDStruct		equ	_ARDStruct	- $$
    dwBaseAddrLow   equ _dwBaseAddrLow  - $$
	dwBaseAddrHigh	equ	_dwBaseAddrHigh	- $$
    dwLengthLow equ _dwLengthLow    - $$
	dwLengthHigh	equ	_dwLengthHigh	- $$
    dwType      equ _dwType     - $$
PageTableNumber		equ	_PageTableNumber- $$
MemChkBuf       equ _MemChkBuf  - $$

DataLen         equ $ - LABEL_DATA
; END of [SECTION .data1]

[SECTION .data2]     ; 数据段
ALIGN   32
[BITS   32]
LABEL_CODE_DATA: 

PagingDemoProc:
OffsetPagingDemoProc    equ PagingDemoProc - $$
    mov eax, LinearAddrDemo
    call    eax
    retf
LenPagingDemoAll    equ $ - PagingDemoProc

foo:
OffsetFoo       equ foo - $$
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, 'F'
    mov [gs:((80 * 17 + 0) * 2)], ax    ; 屏幕第 17 行, 第 0 列。
    mov al, 'o'
    mov [gs:((80 * 17 + 1) * 2)], ax    ; 屏幕第 17 行, 第 1 列。
    mov [gs:((80 * 17 + 2) * 2)], ax    ; 屏幕第 17 行, 第 2 列。
    ret
LenFoo          equ $ - foo

bar:
OffsetBar       equ bar - $$
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, 'B'
    mov [gs:((80 * 18 + 0) * 2)], ax    ; 屏幕第 18 行, 第 0 列。
    mov al, 'a'
    mov [gs:((80 * 18 + 1) * 2)], ax    ; 屏幕第 18 行, 第 1 列。
    mov al, 'r'
    mov [gs:((80 * 18 + 2) * 2)], ax    ; 屏幕第 18 行, 第 2 列。
    ret
LenBar          equ $ - bar

CodeDataLen         equ $ - LABEL_CODE_DATA
; END of [SECTION .data2]

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

TopOfStack  equ $ - LABEL_STACK - 1

; END of [SECTION .gs]

; 堆栈段ring3
[SECTION .s3]
ALIGN   32
[BITS   32]
LABEL_STACK3:
    times 512 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
; END of [SECTION .s3]


; TSS ---------------------------------------------------------------------------------------------
[SECTION .tss]
ALIGN   32
[BITS   32]
LABEL_TSS:
        DD  0           ; Back
        DD  TopOfStack      ; 0 级堆栈
        DD  SelectorStack       ; 
        DD  0           ; 1 级堆栈
        DD  0           ; 
        DD  0           ; 2 级堆栈
        DD  0           ; 
        DD  0           ; CR3
        DD  0           ; EIP
        DD  0           ; EFLAGS
        DD  0           ; EAX
        DD  0           ; ECX
        DD  0           ; EDX
        DD  0           ; EBX
        DD  0           ; ESP
        DD  0           ; EBP
        DD  0           ; ESI
        DD  0           ; EDI
        DD  0           ; ES
        DD  0           ; CS
        DD  0           ; SS
        DD  0           ; DS
        DD  0           ; FS
        DD  0           ; GS
        DD  0           ; LDT
        DW  0           ; 调试陷阱标志
        DW  $ - LABEL_TSS + 2  ; I/O位图基址
        DB  0ffh            ; I/O位图结束标志
TSSLen      equ $ - LABEL_TSS
; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


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


    ; 得到内存数
    mov ebx, 0
    mov di, _MemChkBuf
    .loop:
    mov eax, 0E820h
    mov ecx, 20
    mov edx, 0534D4150h
    int 15h
    jc  LABEL_MEM_CHK_FAIL
    add di, 20
    inc dword [_dwMCRNumber]
    cmp ebx, 0
    jne .loop
    jmp LABEL_MEM_CHK_OK
    LABEL_MEM_CHK_FAIL:
        mov dword [_dwMCRNumber], 0
    LABEL_MEM_CHK_OK:

    ; 初始化 32 位代码段描述符
    InitCodeDescriptor LABEL_SEG_CODE32,LABEL_DESC_CODE32

    ; 初始化数据段描述符
    InitDataDescriptor LABEL_DATA,LABEL_DESC_DATA

    ; 初始化堆栈段描述符
    InitDataDescriptor LABEL_STACK,LABEL_DESC_STACK

    ; 初始化堆栈段描述符(ring3)
    InitDataDescriptor LABEL_STACK3,LABEL_DESC_STACK3

    ; 初始化Ring3描述符
    InitDataDescriptor LABEL_CODE_RING3,LABEL_DESC_CODE_RING3

    ; 初始化 TSS 描述符
    InitDataDescriptor LABEL_TSS,LABEL_DESC_TSS

    ; 初始化测试调用门的代码段描述符
    InitCodeDescriptor LABEL_SEG_CODE_DEST,LABEL_DESC_CODE_DEST

    ; 初始化数据段描述符
    InitDataDescriptor LABEL_CODE_DATA,LABEL_DESC_CODE_DATA

    ; 为加载 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  处


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

LABEL_SEG_CODE32:
    mov ax, SelectorData
    mov ds, ax          ; 数据段选择子
    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
.1:
    lodsb
    test    al, al
    jz  .2
    mov [gs:edi], ax
    add edi, 2
    jmp .1
.2: ; 显示完毕

    ;清屏
    push edi
    mov edi,0
    mov cx,4000
    mov al,0
    cld
.clear: 
    mov [gs:edi],ax
    add edi, 2
    loop .clear
    pop edi

    ; 下面显示一个字符串
    push    szPMMessage
    call    DispStr
    add esp, 4

    push    szMemChkTitle
    call    DispStr
    add esp, 4

    call    DispReturn


    call    DispMemSize     ; 显示内存信息


    call    PagingDemo      ; 演示改变页目录的效果

    ; Load TSS
    mov ax, SelectorTSS
    ltr ax  ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。

    push    SelectorStack3
    push    TopOfStack3
    push    SelectorCodeRing3
    push    0
    retf        ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

    ; 测试调用门(无特权级变换),将打印字母 'C'
    ;call   SelectorCallGateTest:0
    ;call   SelectorCodeDest:0
    ;jmp $


; 启动分页机制 --------------------------------------------------------------
SetupPaging:
    ; 根据内存大小计算应初始化多少PDE以及多少页表
    xor edx, edx
    mov eax, [dwMemSize]
    mov ebx, 400000h    ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
    div ebx
    mov ecx, eax    ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
    test    edx, edx
    jz  .no_remainder
    inc ecx     ; 如果余数不为 0 就需增加一个页表
.no_remainder:
    mov [PageTableNumber], ecx  ; 暂存页表个数

    ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

    ; 首先初始化页目录
    mov ax, SelectorFlatRW
    mov es, ax
    mov edi, PageDirBase0   ; 此段首地址为 PageDirBase0
    xor eax, eax
    mov eax, PageTblBase0 | PG_P  | PG_USU | PG_RWW
.1:
    stosd
    add eax, 4096       ; 为了简化, 所有页表在内存中是连续的.
    loop    .1

    ; 再初始化所有页表
    mov eax, [PageTableNumber]  ; 页表个数
    mov ebx, 1024       ; 每个页表 1024 个 PTE
    mul ebx
    mov ecx, eax        ; PTE个数 = 页表个数 * 1024
    mov edi, PageTblBase0   ; 此段首地址为 PageTblBase0
    xor eax, eax
    mov eax, PG_P  | PG_USU | PG_RWW
.2:
    stosd
    add eax, 4096       ; 每一页指向 4K 的空间
    loop    .2

    mov eax, PageDirBase0
    mov cr3, eax
    mov eax, cr0
    or  eax, 80000000h
    mov cr0, eax
    jmp short .3
.3:
    nop

    ret
; 分页机制启动完毕 ----------------------------------------------------------


; 测试分页机制 --------------------------------------------------------------
PagingDemo:
    mov ax, SelectorCodeData
    mov ds, ax
    mov ax, SelectorFlatRW
    mov es, ax

    push    LenFoo
    push    OffsetFoo
    push    ProcFoo
    call    MemCpy
    add esp, 12

    push    LenBar
    push    OffsetBar
    push    ProcBar
    call    MemCpy
    add esp, 12

    push    LenPagingDemoAll
    push    OffsetPagingDemoProc
    push    ProcPagingDemo
    call    MemCpy
    add esp, 12

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

    call    SetupPaging     ; 启动分页

    call    SelectorFlatC:ProcPagingDemo
    call    PSwitch         ; 切换页目录,改变地址映射关系
    call    SelectorFlatC:ProcPagingDemo

    ret
; ---------------------------------------------------------------------------


; 切换页表 ------------------------------------------------------------------
PSwitch:
    ; 初始化页目录
    mov ax, SelectorFlatRW
    mov es, ax
    mov edi, PageDirBase1   ; 此段首地址为 PageDirBase1
    xor eax, eax
    mov eax, PageTblBase1 | PG_P  | PG_USU | PG_RWW
    mov ecx, [PageTableNumber]
.1:
    stosd
    add eax, 4096       ; 为了简化, 所有页表在内存中是连续的.
    loop    .1

    ; 再初始化所有页表
    mov eax, [PageTableNumber]  ; 页表个数
    mov ebx, 1024       ; 每个页表 1024 个 PTE
    mul ebx
    mov ecx, eax        ; PTE个数 = 页表个数 * 1024
    mov edi, PageTblBase1   ; 此段首地址为 PageTblBase1
    xor eax, eax
    mov eax, PG_P  | PG_USU | PG_RWW
.2:
    stosd
    add eax, 4096       ; 每一页指向 4K 的空间
    loop    .2

    ; 在此假设内存是大于 8M 的
    mov eax, LinearAddrDemo
    shr eax, 22
    mov ebx, 4096
    mul ebx
    mov ecx, eax
    mov eax, LinearAddrDemo
    shr eax, 12
    and eax, 03FFh  ; 1111111111b (10 bits)
    mov ebx, 4
    mul ebx
    add eax, ecx
    add eax, PageTblBase1
    mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW

    mov eax, PageDirBase1
    mov cr3, eax
    jmp short .3
.3:
    nop

    ret
; ---------------------------------------------------------------------------




; 显示内存信息 --------------------------------------------------------------
DispMemSize:
    push    esi
    push    edi
    push    ecx

    mov esi, MemChkBuf
    mov ecx, [dwMCRNumber]  ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
.loop:                  ;{
    mov edx, 5          ;   for(int j=0;j<5;j++)    // 每次得到一个ARDS中的成员,共5个成员
    mov edi, ARDStruct      ;   {           // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:                 ;
    push    dword [esi]     ;
    call    DispInt         ;       DispInt(MemChkBuf[j*4]); // 显示一个成员
    pop eax         ;
    stosd               ;       ARDStruct[j*4] = MemChkBuf[j*4];
    add esi, 4          ;
    dec edx         ;
    cmp edx, 0          ;
    jnz .1          ;   }
    call    DispReturn      ;   printf("\n");
    cmp dword [dwType], 1   ;   if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
    jne .2          ;   {
    mov eax, [dwBaseAddrLow]    ;
    add eax, [dwLengthLow]  ;
    cmp eax, [dwMemSize]    ;       if(BaseAddrLow + LengthLow > MemSize)
    jb  .2          ;
    mov [dwMemSize], eax    ;           MemSize = BaseAddrLow + LengthLow;
.2:                 ;   }
    loop    .loop           ;}
                    ;
    call    DispReturn      ;printf("\n");
    push    szRAMSize       ;
    call    DispStr         ;printf("RAM size:");
    add esp, 4          ;
                    ;
    push    dword [dwMemSize]   ;
    call    DispInt         ;DispInt(MemSize);
    add esp, 4          ;

    pop ecx
    pop edi
    pop esi
    ret
; ---------------------------------------------------------------------------

%include "lib.inc"

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

[SECTION .sdest]; 调用门目标段
[BITS   32]

LABEL_SEG_CODE_DEST:
;mov    ax, SelectorVideo
;mov    gs, ax          ; 视频段选择子(目的)
;
;mov    edi, (80 * 12 + 0) * 2  ; 屏幕第 12 行, 第 0 列。
;mov    ah, 0Ch         ; 0000: 黑底    1100: 红字
;mov    al, 'C'
;mov    [gs:edi], ax

    retf

SegCodeDestLen  equ $ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]



; CodeRing3
[SECTION .ring3]
ALIGN   32
[BITS   32]
LABEL_CODE_RING3:
;mov    ax, SelectorVideo
;mov    gs, ax          ; 视频段选择子(目的)
;
;mov    edi, (80 * 14 + 0) * 2  ; 屏幕第 14 行, 第 0 列。
;mov    ah, 0Ch         ; 0000: 黑底    1100: 红字
;mov    al, '3'
;mov    [gs:edi], ax

    call    SelectorCallGateTest:0  ; 测试调用门(有特权级变换),将打印字母 'C'。
    jmp $
SegCodeRing3Len equ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值