LDT(Local Descriptor Table)它和GDT差不多,都是描述符表(Descriptor Table)区别仅仅在于全局(Global)和局部(Local)的不同。局部描述符表可以有若干张,每个任务可以有一张。

LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。

; ==========================================
; pmtest3.asm
; 编译方法:nasm pmtest3.asm -o pmtest3.com
; ==========================================;常量, 宏, 以及一些说明
DA_32       EQU    4000h   ; 32 位段
DA_C        EQU    98h     ; 存在的只执行代码段属性值
DA_LDT      EQU    82h     ; 局部描述符表段类型值
DA_DRW      EQU    92h     ; 存在的可读写数据段属性值
DA_DRWA     EQU    93h     ; 存在的已访问可读写数据段类型值
ATCE32      EQU    4098h   ;存在的只执行32代码段属性值
DA_DPL1     EQU    20h     ; DPL = 1SA_TIG     EQU 0      ;将TI位置0
SA_TIL      EQU 4      ;将TI位置1
;下面是宏定义
; 有三个参数:段界限,段基址,段属性其中宏定义中的%1代表参数1,%2代表参数2,%3代表参数3
%macro Descriptor 3    dw  %2 & 0FFFFh             ; 段界限1(参数2的低16位)  dw  %1 & 0FFFFh             ; 段基址1(参数1的低16位)  db  (%1 >> 16) & 0FFh       ; 段基址2(参数1的16-23位)  dw  ((%2 >> 8) & 0F00h) | (%3 & 0F000h)| (%3 & 000FFh) ; 属性1(高4位) + 段界限2(高4位) + 属性2(低8位)  db  (%1 >> 24) & 0FFh       ; 段基址3(参数1的24-31位)
%endmacro ; 共 8 字节
;段界限共20位,段基地址30位,段属性共16位(含段界限高4位)
org 0100hjmp    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+DA_DPL1; Data
LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位
LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT    ; LDT
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
SelectorLDT     equ LABEL_DESC_LDT      - 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 - $$                  ;PMMessage起始地址偏移
PMMessage2:     db  "hello world!"
PMMessage2Len       equ     $ - PMMessage2OffsetPMMessage2  equ PMMessage2 - $$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, csmov   ds, axmov   es, axmov   ss, axmov   sp, 0100hmov    [LABEL_GO_BACK_TO_REAL+3], ax;如果不懂请看前面文章分析mov  [SPValueInRealMode], sp; 初始化 16 位代码段描述符mov  ax, csmovzx eax, ax ;零扩展指令shl   eax, 4  ;段值*16 add  eax, LABEL_SEG_CODE16               ;段值*16+偏移=16位代码段基地址 mov   word [LABEL_DESC_CODE16 + 2], ax    ;基地址1  shr eax, 16mov  byte [LABEL_DESC_CODE16 + 4], al    ;基地址2mov   byte [LABEL_DESC_CODE16 + 7], ah    ;基地址3; 初始化 32 位代码段描述符xor   eax, eaxmov ax, csshl   eax, 4   ;段值*16 add eax, LABEL_SEG_CODE32  ;段值*16+偏移=16位代码段基地址 mov    word [LABEL_DESC_CODE32 + 2], ax  ;基地址1shr eax, 16mov  byte [LABEL_DESC_CODE32 + 4], al   ;基地址2mov    byte [LABEL_DESC_CODE32 + 7], ah   ;基地址3; 初始化数据段描述符xor eax, eaxmov ax, dsshl   eax, 4   ;段值*16 add eax, LABEL_DATA  ;段值*16+偏移=16位代码段基地址 mov  word [LABEL_DESC_DATA + 2], ax   ;基地址1shr  eax, 16mov  byte [LABEL_DESC_DATA + 4], al   ;基地址2mov  byte [LABEL_DESC_DATA + 7], ah   ;基地址3; 初始化堆栈段描述符xor   eax, eaxmov ax, dsshl   eax, 4   ;段值*16 add eax, LABEL_STACK  ;段值*16+偏移=16位代码段基地址 mov word [LABEL_DESC_STACK + 2], axshr eax, 16mov  byte [LABEL_DESC_STACK + 4], almov byte [LABEL_DESC_STACK + 7], ah; 初始化 LDT 在 GDT 中的描述符xor    eax, eaxmov ax, dsshl   eax, 4  ;段值*16add   eax, LABEL_LDT  ;段值*16+偏移=16位代码段基地址 (LDT表基地址)mov  word [LABEL_DESC_LDT + 2], ax ;基地址1shr eax, 16mov  byte [LABEL_DESC_LDT + 4], al  ;基地址2mov    byte [LABEL_DESC_LDT + 7], ah  ;基地址3; 初始化 LDT 中的描述符xor eax, eaxmov ax, dsshl   eax, 4   ;段值*16 add eax, LABEL_CODE_A  ;段值*16+偏移=16位代码段基地址 (LDT表对应的代码段的基地址)mov  word [LABEL_LDT_DESC_CODEA + 2], ax  ;基地址1shr  eax, 16mov  byte [LABEL_LDT_DESC_CODEA + 4], al  ;基地址2mov  byte [LABEL_LDT_DESC_CODEA + 7], ah  ;基地址3xor  eax, eaxmov ax, dsshl   eax, 4   ;段值*16 add eax, LABEL_CODE_B  ;段值*16+偏移=16位代码段基地址 (LDT表对应的代码段的基地址)mov  word [LABEL_LDT_DESC_CODEB + 2], ax  ;基地址1shr  eax, 16mov  byte [LABEL_LDT_DESC_CODEB + 4], al  ;基地址2mov  byte [LABEL_LDT_DESC_CODEB + 7], ah  ;基地址3; 为加载 GDTR 作准备xor    eax, eaxmov ax, dsshl   eax, 4  ;基地址2add    eax, LABEL_GDT      ; eax <- gdt 基地址mov  dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt  [GdtPtr]; 关中断cli; 打开地址线A20in    al, 92hor   al, 00000010bout    92h, al; 准备切换到保护模式mov   eax, cr0or  eax, 1mov   cr0, eax; 真正进入保护模式jmp   dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:        ; 从保护模式跳回到实模式就到了这里mov   ax, csmov   ds, axmov   es, axmov   ss, axmov   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, SelectorDatamov ds, ax          ; 数据段选择子mov ax, SelectorVideomov    gs, ax          ; 视频段选择子mov ax, SelectorStackmov    ss, ax          ; 堆栈段选择子mov esp, TopOfStack; 下面显示一个字符串mov   ah, 0Ch         ; 0000: 黑底    1100: 红字xor   esi, esixor edi, edimov esi, OffsetPMMessage    ; 源数据偏移mov  edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。cld    ;设置数据传输方向
.1:lodsb                 ;从[ds:esi]中去一个字符送入al  test al, al        ;判断是否到字符串末尾(以0结尾)  jz   .2            ;是->跳转  mov    [gs:edi], ax  ;否->输入到显存add   edi, 2        ;指针加2(一个字符占两个字节)jmp .1
.2: ; 显示完毕call  DispReturn    ;换行  ; Load LDTmov    ax, SelectorLDT   lldt  ax      ;加载LDT在GDT中的描述符call SelectorLDTCodeB:0      ; 跳入局部任务push    SelectorLDTCodeAxor eax,eaxpush eaxretf ; 跳入局部任务;jmp    SelectorLDTCodeA:0
; ------------------------------------------------------------------------
DispReturn:push eaxpush ebxmov  eax, edi ;获取当前光标的显存偏移  mov  bl, 160  ;每行80字符,每个字符占两个字节(字符ASCII码+字符属性),所以一行共80*2=160个字节  div bl       ;获取当前行数(在显存中行数从0开始编号的)and    eax, 0FFh ;取低8位(在当前页,否则有可能输出到其它页了,显示器不会显示的)inc  eax      ;下一行mov    bl, 160mul  bl       ;确定下一行开始的字节数mov    edi, eax ;更新edi寄存器pop   ebxpop  eaxret
; 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, axmov   es, axmov   fs, axmov   gs, axmov   ss, axmov   eax, cr0and al, 11111110b;设置cr0的0位(PE位,PE=0准备进入实模式)  mov  cr0, eax     ;更新cr0LABEL_GO_BACK_TO_REAL:jmp    0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值Code16Len   equ $ - LABEL_SEG_CODE16; END of [SECTION .s16code]; LDT
[SECTION .ldt]
ALIGN   32
LABEL_LDT:
;                            段基址       段界限      属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位
LABEL_LDT_DESC_CODEB: Descriptor 0, CodeBLen - 1, DA_C + DA_32 ; Code, 32 位
LDTLen      equ $ - LABEL_LDT ;LDT表长度; LDT 选择子
SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL
SelectorLDTCodeB    equ LABEL_LDT_DESC_CODEB    - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN   32
[BITS   32]
LABEL_CODE_A:mov    ax, SelectorVideomov    gs, ax          ; 视频段选择子(目的)mov edi, (80 * 12 + 0) * 2 ; 屏幕第 12 行, 第 0 列。mov   ah, 0Ch         ; 0000: 黑底    1100: 红字mov   al, 'L'mov    [gs:edi], ax; 准备经由16位代码段跳回实模式jmp    SelectorCode16:0
CodeALen    equ $ - LABEL_CODE_A
; END of [SECTION .la]; CodeB (LDT, 32 位代码段)
[SECTION .lb]
ALIGN   32
[BITS   32]
LABEL_CODE_B:mov    ax, SelectorVideomov    gs, ax          ; 视频段选择子(目的)mov     ax,SelectorData;mov     ds,axmov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写mov     ecx,PMMessage2Lenmov edi, (80 * 11 + 0) * 2 ; 屏幕第 11 行, 第 0 列。mov   ah, 0Ch         ; 0000: 黑底    1100: 红字.loop  mov     al,[ds:esi]mov [gs:edi], axadd     edi,2inc esiloop .loopretf
CodeBLen    equ $ - LABEL_CODE_B
; END of [SECTION .lb]

在这里有两个局部任务,分别对应两个ldt表,一个局部任务是输出字符‘L’,一个任务是输出‘hello world!’,当然还有一个全局任务,输入字符‘In Protect Mode now.’程序先在实模式下初始化GDT,LDT表,设置相关寄存器,进入保护模式,输出一个字符串,然后加载局部描述表LDT,进入局部任务B,执行后返回,然后执行任务A,任务A完成后跳入16位代码段,设置相关寄存器值,回到实模式,结束回到DOS.

在进入保护模式前,我们需要初始化GDT,LDT表,因为在保护模式下需要根据相应段的表,找到代码或者数据的位置,当有局部任务时,GDT中应该包含LDT表存放的位置,这样当处理器在段描述符表中找到LDT描述表后发现是局部任务,这样就可以通过其记录的LDT表的位置,找到LDT表,然后在LDT表中找到相应的段描述符,如下图所示:

段描述符表=全局任务(GDT+LDT)GDTR寄存器中用于存放全局描述符表GDT的32位线性基地址和16位的表的长度值。LDTR寄存器中用于存放局部描述符表LDT的32位线性基地址和16位的表的长度值。通过系统指令,lgdt将GDT的线性基址和长度值加载到GDTR寄存器中,lldt将LDT的线性基址和长度值加载到LDTR寄存器中。

下面解释部分代码:

call SelectorLDTCodeB:0      ; 跳入局部任务

通过call 进入局部任务B(段间转移),完成相应字符输出。

LABEL_CODE_B:mov ax, SelectorVideomov    gs, ax          ; 视频段选择子(目的)mov     ax,SelectorData;mov     ds,axmov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写mov     ecx,PMMessage2Lenmov edi, (80 * 11 + 0) * 2 ; 屏幕第 11 行, 第 0 列。mov   ah, 0Ch         ; 0000: 黑底    1100: 红字.loop  mov     al,[ds:esi]mov [gs:edi], axadd     edi,2inc esiloop .loopretf 

任务B中初始化相应段寄存器,设置字符串指针请注意下面语句:

mov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写

不能用后面注释的语句,后面的语句实在实模式下的偏移地址,在保护模式中用的是相对于当前节(SECTION)的偏移地址,这个在前面也介绍过,可以参考前面文章。
任务B完成后通过retf返回(还原段选择子和偏移)。接下来准备进入任务A

        push    SelectorLDTCodeAxor eax,eaxpush eaxretf  ; 跳入局部任务;jmp    SelectorLDTCodeA:0</span>

这里模拟是call—retf指令,把段选择子和偏移压入栈中,通过retf用栈中的数据设置cs,ip,达到跳转的目的。 这就是两个局部任务A和B。

下面这段代码是在LDT表中"声明"任务A,B,分别指向相应代码段,然后设置相应选择子:

; LDT
[SECTION .ldt]
ALIGN   32
LABEL_LDT:
;                            段基址       段界限      属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位
LABEL_LDT_DESC_CODEB: Descriptor 0, CodeBLen - 1, DA_C + DA_32 ; Code, 32 位
LDTLen      equ $ - LABEL_LDT ;LDT表长度; LDT 选择子
SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL
SelectorLDTCodeB    equ LABEL_LDT_DESC_CODEB    - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]

纯DOS下运行结果:

《Orange's 一个操作系统的实现》学习笔记--LDT相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. 剑指offer面试题6:重建二叉树
  2. 服务发现 注册中心 consul 的介绍、部署和使用
  3. Address already in use: JVM_Bind 端口被占用解决办法
  4. 全球及中国服务器电源行业市场深度策略分析及投资规划咨询建议报告2022-2028年版
  5. Ubuntu下使用VSCode的launch.json及tasks.json编写
  6. linux mv 保持目录结构_Linux中的mv命令详解
  7. springboot主要注解及其作用
  8. 链表面试题1:反转单链表,不带头结点。
  9. 查看进程占用,并kill掉
  10. 修改oracle数据库内存参数,物理内存扩容,oracle 11g R1数据库相关参数修改
  11. luogu P1273 有线电视网
  12. 解决安装Discuz!插件时提示“对不起,您安装的不是正版应用,安装程序无法继续执行”的方法...
  13. matlab自适应遗传算法代码,自适应遗传算法MATLAB代码
  14. 微信小程序使用IconFont彩色图标
  15. 判断一颗二叉树是否为二叉平衡树 python 代码
  16. Ubuntu打开wps文件缺少字体
  17. 第六章 姜小白当机立断逃莒国 姜诸儿瓜期不代终遇鬼
  18. 微博自动同步发表到微头条工具使用说明
  19. 开发一个会员管理系统
  20. arcgis绘核密度分布图

热门文章

  1. android 点击跳转到qq,android开发使用WebView点击网页链接跳转至QQ临时会话
  2. proxifier注意事项
  3. 矩形脉冲用matlab,矩形脉冲波形的占空比
  4. linux下阅读.caj文件
  5. ElasticSearch聚合查询返回结果buckets取值
  6. 融资前和投资人的一次讨论
  7. CSS背景相关设置,元素显示模式,三大特性
  8. 给宝宝早教c语言,分享:0-3岁婴幼儿语言发展与教育
  9. Android Studio 禁止光标自动移动到首行位置
  10. Android高级开发书籍