学习自 狄泰软件

1 EAX与AX不是独立的,EAX是32位的寄存器,而AX是EAX的低16位。

2 or 对两个操作数进行逻辑(按位)或操作

3
word 两个字节 16位
dword 四个字节 32位


80286 虽然有了保护模式,但其依然是 16 位的 CPU ,其通用寄存器还是 16 位宽,但其与 8086 不同的是其地址线由 20 位变为了 24 位,即寻址空间变成了 24 次方,等于 16MB 大小。虽然80286可以将地址线变成了24位,可以访问16MB的内存,但是其用来寻址的通用寄存器还是16位的,也没有突破一个寄存器只能访问64kb空间的限制,如果用寄存器来进行地址访问,那么,想访问16MB的内存,就需要不断地变换段基址,所以很快就被淘汰了。

80286的缺陷
单独的一个寄存器无法访问到全部内存空间, 也就是若用寄存器存储段内偏移地址
只能访问到 64kb大小的段。

改革背景
每次 CPU 变革的原因几乎都是地址总线宽度不够导致的,即内存需求越来越大,干脆直接将地址线直接改成32位的,可以访问4GB的内存地址
1985年推出的第一个32位的微处理器,它的地址总线和寄存器都是32位的
段基址是32位的,寄存器也是32位的,这样在任意一个段都可以访问到4GB的空间了,甚至段基址地址可以是0,直接用段偏移地址就可以4GB空间的任意角落,这就开启了平坦模式的时代


8086
80286
80386
X指的是 该处理器的版本

在保护模式下,定义一个段,必须要提供段的三个要素:
1 段的起始地址
2 段的界限(段内的偏移地址的最大值)
3 段属性(如 我们平时所用的 代码段是只读的,那么它是怎么被指定成只读的呢? 就是依靠段属性!!! DA_DR标识符)

选择子的本质就是 索引,这个索引特别的是分为两部分,第一部分就是传统的索引,它的值就是0 1 2 3 4 5 … 指的就是段描述符表当中的第0项 第1项 第2项 …第n项等等,相当于数组下标。第二部分是特殊部分 ,选择子的属性:
PRL : 占用 第0位 – 第1位 是关于特权级属性,占用两位,表示4个值 0 1 2 3 ,四个级别
TI : 占用1位,是第三位 所以代表 0 或者 4

0 : GDT   表示该选择子想要去访问的段描述符是 全局段描述符
4 : LDT    表示该选择子想要去访问的段描述符是 局部段描述符

; 描述符
; 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 & 0xFFFF ; 段界限1 将第2个参数,段界限的低16位 排放在段描述符这8个字节的前16位上 0 - 15
dw %1 & 0xFFFF ; 段基址1 将第1个参数,段基址的低16位 排放在段描述符这8个字节的 16 - 31 位
db (%1 >> 16) & 0xFF ; 段基址2 将第1个参数,段基址第16位 – 第23位 共8位 排放在段描述符这8个字节的 32 - 39 位
; %3 & 0xF0FF 将第三个参数 段属性 的低8位 排放 段描述符的 40 - 47 位。 高4位放在 段描述符的 52 - 55 位
; (%2 >> 8) & 0xF00 将将第2个参数 段界限的 16 - 19 位 放在 段描述符的 48 - 51位
dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2
db (%1 >> 24) & 0xFF ; 段基址3 将第1个参数,段基址的高8位 24 - 31 排放在段描述符这8个字节 24 - 31位
%endmacro ; 共 8 字节


Descriptor 宏定义

; GDT_ENTRY标签 是全局段描述符表GDT 的入口地址
; 段基址, 段界限, 段属性
GDT_ENTRY : Descriptor 0, 0, 0
; ODE32_DESC 标签 (DA_C + DA_32 : 32位模式(保护模式)下的段,具有只执行代码段的属性)
CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32

;计算全局段描述附表的长度 = 当前行偏移地址 - 全局段描述符表入口地址
GdtLen equ $ - GDT_ENTRY

;可以看做一个结构体 第一个成员指定 全局段描述符表的界限,即相对入口地址 偏移的最大值。
;段描述符表 的 标识数据结构 用于加载段描述符表
GdtPtr:
dw GdtLen - 1
;GDT基地址 需要重新计算
dd 0

定义代码段,section 所定义的代码段仅限于原代码里的代码段,是未经编译的以文本的形式存在的代码段。

定义两个代码节,S1代码节先出现,所以它里面的代码先被存放,01 02 00 00,至于后面两个字节的 00,是由于代码节之间的内存对齐,从第一个代码节 切换到 第二个代码节的时候 必须四字节对齐。如下面的两个代码节 .s1 .s2 ,那么 从.s1 切换到 .s2 的时候 必须是保证四字节对齐。所以.s1 代码节所占用的内存数必须是4的整数倍, 没有的填0补齐。所以编译后 得到的结果就是右图



1
2 保护模式是从实模式转换进入的,在默认情况下就是实模式,而实模式中就是 16位的数据和代码
3 必须使用 无条件跳转指令jmp 从16位代码段 跳转到32位 代码段




makefile

.PHONY : all clean rebuildBOOT_SRC := boot.asm
BOOT_OUT := bootLOADER_SRC  := loader.asm
INCLUDE_SRC := inc.asm
LOADER_OUT  := loaderIMG := data.img
IMG_PATH := /mnt/hgfsRM := rm -frall : $(IMG) $(BOOT_OUT) $(LOADER_OUT)@echo "Build Success ==> D.T.OS!"$(IMG) :bximage $@ -q -fd -size=1.44$(BOOT_OUT) : $(BOOT_SRC)nasm $^ -o $@dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc$(LOADER_OUT) : $(LOADER_SRC) $(INCLUDE_SRC)nasm $< -o $@sudo mount -o loop $(IMG) $(IMG_PATH)sudo cp $@ $(IMG_PATH)/$@sudo umount $(IMG_PATH)clean :$(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)rebuild :@$(MAKE) clean@$(MAKE) all

代码段位于 代码段,段基址位于 代码段寄存器 CS
全局段描述符表 位于数据段,段基址位于 数据段寄存器 DS

loader.asm

%include "inc.asm"
;程序起始地址
org 0x9000;无条件跳转到  CODE16_SEGMENT标签处执行
jmp CODE16_SEGMENT;定义 全局段描述符表, 描述符表中的第0个描述符不使用,仅用于占位
;定义一个名为 .gdt 的逻辑代码段 ,是源码级别的代码段
[section .gdt]
;                                 段基址,       段界限,       段属性;段描述符表 第0项
;定义一个段描述符GDT_ENTRY GDT_ENTRY标签 全局段描述符GDT 的入口地址,第0项不使用 仅用于占位 所以设置为0
GDT_ENTRY       :     Descriptor    0,            0,           0;段描述符表 第1项
;定义一个段描述符CODE32_DESC  CODE32_DESC标签
;属性:32位模式(保护模式)下的段,具有只执行代码段的属性
CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
; GDT end;该全局段描述附表的长度 = 当前行偏移地址 - 全局段描述符表入口地址
GdtLen    equ   $ - GDT_ENTRY;可以看做一个结构体 第一个成员指定 全局段描述符表的界限,即相对入口地址 偏移的最大值。
;记录全局段描述符表 起始地址
;段描述符表 的 标识数据结构 用于加载段描述符表
GdtPtr:; GDT 界限 两个字节 dwdw   GdtLen - 1; GDT 基地址 四个字节 dddd   0; GDT Selector
; 定义 段描述符CODE32_DESC 的选择子
; 段描述符索引值是 1 , 属性是 SA_TIG(表示该选择子想要去访问的段描述符是 全局段描述符) + SA_RPL0(特权级 第0特权级)Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0; end of [section .gdt]   全局段描述附表 结束;定义实模式代码段 16位代码段,需要按照16位的方式进行编译 所以只是编译器按照16位方式进行编译 [bits 16]
[section .s16]
[bits 16]
CODE16_SEGMENT:mov ax, csmov ds, axmov es, axmov ss, ax;定义栈顶mov sp, 0x7c00; initialize GDT for 32 bits code segment  ; 设置32位段基址 到 段描述符;将当前代码段寄存器值 左移4位mov eax, 0mov ax, cs;eax 左移4位shl eax, 4;段寄存器<<4) + (32位代码段 偏移地址   == 32位代码段的真实物理地址,即段基地址add eax, CODE32_SEGMENT;将 ax寄存器保存的值 拿两个字节 放到 CODE32_DESC+2 地址处, 段基址的0-15位 放到 段描述符的16-31 位mov word [CODE32_DESC + 2], ax;eax 右移16位  也就是低16位被移出去了shr eax, 16; al(ax低8位)   此时al是32位代码段基地址当中的第三个字节(段基址 16-23位) 放到 段描述符的32-39位mov byte [CODE32_DESC + 4], al; ah(ax高8位)    此时ah是32位代码段基地址当中第四个字节(段基址 24-31位) 放到 段描述符的55-63位 第七个字节mov byte [CODE32_DESC + 7], ah; initialize GDT pointer struct ; 全局段描述符表 起始地址mov eax, 0mov ax, ds;将 段寄存器值 左移4位shl eax, 4;段寄存器<<4) + (段描述符GDT_ENTRY 偏移地址   == 段描述符GDT_ENTRY 的真实物理地址add eax, GDT_ENTRY; 段描述符GDT_ENTRY 的真实物理地址 放到  GdtPtr 结构体第二个字节出 代表 GDT 基地址mov dword [GdtPtr + 2], eax; 1. load GDT  加载全局段描述符表   lgdt指定 GdtPtr结构lgdt [GdtPtr]; 2. close interrupt  关闭中断,因为现在马上要跳转到保护模式了cli ; 3. open A20  打开 A20 地址线in al, 0x92or al, 00000010bout 0x92, al; 4. enter protect mode  通知处理器进入保护模式  将某个寄存器对应位置1mov eax, cr0or eax, 0x01mov cr0, eax; 5. jump to 32 bits code  从16位的实模式 跳转到 32位的保护模式; 注意 这里使用的是选择子,用来访问 全局段描述符表里面 第2项段描述符 CODE32_DESC段描述符 它记录了32位代码段(保护模式代码段)的起始地址,界限,属性等等。; 使用选择子进行跳转 ,得到32位代码段 段描述符的内容(CODE32_DESC),根据内容 得到段基址 再加上段内偏移地址0 就是真实的32位代码段的入口地址jmp dword Code32Selector : 0;定义保护模式代码段 32位代码段,需要按照32位的方式进行编译 所以只是编译器按照32位方式进行编译 [bits 32]
[section .s32]
[bits 32]
; 32位代码段 段基址
CODE32_SEGMENT:mov eax, 0jmp CODE32_SEGMENTCode32SegLen    equ    $ - CODE32_SEGMENT

inc.asm

; Segment Attribute  段属性定义
DA_32    equ    0x4000
DA_DR    equ    0x90
DA_DRW   equ    0x92
DA_DRWA  equ    0x93
DA_C     equ    0x98
DA_CR    equ    0x9A
DA_CCO   equ    0x9C
DA_CCOR  equ    0x9E; Selector Attribute 选择子属性定义
SA_RPL0    equ    0
SA_RPL1    equ    1
SA_RPL2    equ    2
SA_RPL3    equ    3SA_TIG    equ    0
SA_TIL    equ    4; 描述符   段描述符所需要的宏
; 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 & 0xFFFF                         ; 段界限1dw    %1 & 0xFFFF                         ; 段基址1db    (%1 >> 16) & 0xFF                   ; 段基址2dw    ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2db    (%1 >> 24) & 0xFF                   ; 段基址3
%endmacro                                     ; 共 8 字节

80286 与 80386,实模式与保护模式切换编程相关推荐

  1. 深入理解计算机系统-之-内存寻址(二)--存储保护机制(CPU实模式与保护模式)

    cpu的保护模式由来 分段机制 8086的诞生,标志着Intel 正式进入了x86时代,这是个多么具有纪念意义的日子:1978-6-8.同时,8086的诞生也是处理器内存寻址技术的第一次飞跃. 对于一 ...

  2. ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构

    ★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...

  3. 实模式和保护模式区别及寻址方式

    64KB-4GB-64TB? 我记得大学的汇编课程.组成原理课里老师讲过实模式和保护模式的区别,在很多书本上也有谈及,无奈本人理解和感悟能力实在太差,在很长一段时间里都没真正的明白它们的内含,更别说为 ...

  4. MIT-JOS系列1:实模式和保护模式下的段寻址方式

    实模式下的段寻址 以8086为例 8086 段寄存器16位(段地址/基地址),寄存器16位(偏移地址),地址总线20位(寻址1M:2^20) 实际物理地址 = (段寄存器 << 4) + ...

  5. Linux下的实模式和保护模式

    实模式:(即实地址访问模式)它是Intel公司80286及以后的x86(80386,80486和80586等)兼容处理器(CPU)的一种操作模式.实模式被特殊定义为20位地址内存可访问空间上,这就意味 ...

  6. x86从实模式到保护模式 pdf_【自制操作系统04】从实模式到保护模式

    通过前三章的努力,我们成功将控制权转交给了 loader.asm 这个程序.具体说就是 bios 通过加载并跳转到 0x7c00(IMB大叔们定的) 把控制权转交给了我们操作系统的第一个汇编程序 mb ...

  7. X86汇编语言从实模式到保护模式11:指令格式及操作尺寸

    目录 1. 80286的16位保护模式 1.1 80286寄存器 1.2 80286段描述符 1.3 80286保护模式内存访问 2. 指令操作尺寸 2.1 指令操作尺寸的概念 2.2 16位处理器的 ...

  8. 【操作系统 3.了解实模式与保护模式的区别】

    一.实模式与保护模式鸟瞰 我这人喜欢直面问题,其实本章只需要搞明白三个主要问题就行了, 什么是实模式和保护模式, 实模式与保护模式的区别是什么, 怎么进入保护模式. 我先来简单阐述下这三个问题 什么是 ...

  9. X86实模式与保护模式简介

    0 引言 从80386开始,CPU有三种工作方式:实模式,保护模式和虚拟8086模式(v86模式).只有在刚刚启动的时候是real-mode,等到操作系统运行起来以后就切换到protected-mod ...

  10. 实模式、保护模式和虚拟8086模式

    参考自:实模式与保护模式解惑之(一)--二者的起源与区别(河西无名式) 概述:实模式和保护模式是处理器发展的两个非常重要的阶段.这两个模式下的编程也有着显著的不同,弄明实模式与保护模式的区别是理解操作 ...

最新文章

  1. Hide the common top menu in Ubuntu 12.04
  2. 【运维】阿里云宝塔面板部署JavaWeb项目
  3. SerfJ REST
  4. libgdx 3D 测试一
  5. Java实现Runnable接口创建多线程
  6. Github 大牛封装 Python 代码,实现自动发送邮件只需三行代码
  7. 欢迎来到我的第一个个人laravel尝试论坛项目,给予评价和建议 谢谢。
  8. 电脑pin码忘了登录不进系统_想要大屏打王者,家里电视用不了?投屏到电脑试试...
  9. 阅读总结:如何在生产中成功运用Docker
  10. (九)linux中断编程
  11. HDU 2340 - Obfuscation(dp)
  12. 【SQL】CONNECT BY 层次化查询
  13. easymock使用方法_EasyMock静态方法– PowerMock,JUnit 4,TestNG
  14. ssm整合(crm案例)
  15. 利用shell删除labelme打错的标签
  16. “造星工厂“乐华娱乐IPO,如何寻找下一个“王一博“?
  17. 深入了解Unity剔除(草稿)
  18. Python简单绘制柱状图
  19. pygame小游戏——中国地图拼图小游戏
  20. 《Ensemble Learning Methods for Deep Learning Neural Networks》笔记

热门文章

  1. 解决win10自动锁屏问题的一个方法
  2. 1-MySQL事务特性
  3. 计算机历史记录无法删除,win10时间线灰色浏览记录删不掉怎么回事_win10时间线历史灰色无法删除的解决教程-win7之家...
  4. 计算机怎么快捷截图桌面,屏幕截屏的快捷键是什么 6种电脑截图快捷键操作方法...
  5. 大胖子走迷宫【第十届】【决赛】
  6. python多条件选股_通达信几种实用的条件选股公式,一旦掌握,至少翻翻!
  7. python 绘图库_Python安装可视化绘图库,你真的会了吗?一文告诉你全部
  8. 阿里巴巴高级Java面试题(首发,70道)
  9. UltraISO 软碟通制作 Windows 7 系统 U 盘启动盘
  10. 需求分析与原型图设计