ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

《x86汇编语言:从实模式到保护模式》笔记

2022-01-27 21:34:50  阅读:369  来源: 互联网

标签:保护模式 x86 16 地址 指令 偏移 内存 从实 寄存器


x86汇编语言笔记

8086通用寄存器

16位寄存器:AX、BX、CX、DX、SI、DI、BP、SP。

前4个可分为高8位和低8位来使用:AH、AL、BH、BL、CH、CL、DH、DL。

内存分段

采用分段技术解决地址重定位问题,在硬件级别用两个段寄存器来支持,代码段寄存器CS和数据段寄存器DS。

实模式下CPU访问物理地址的方式:段基址:偏移地址

8086段寄存器

  • 代码段寄存器:CS
  • 数据段寄存器:DS
  • 附加段寄存器:ES
  • 栈段寄存器:SS

如何访问指令?

当程序开始时,CS指向代码段的起始地址,IP指令指针指向段内偏移。即CS:IP

注意:一般没有指定段寄存器时,默认使用DS。

如何访问数据?

在访问内存单元时,则采用DS:偏移地址

注意1:由于8086提供20位的物理地址,所以在计算物理地址时,要将段寄存器左移4位再加上偏移地址。由此可以得出8086最大只能访问1MB的内存空间。

注意2:内存是以字节为单位,内存中的每一个字节都对应一个物理地址。

计算机的启动流程

image-20220122202902641

注意:主引导扇区的最有两个字节是0x55和0xaa。

显存

显存存在于显卡之中,一般CPU是通过把显存映射到0xB8000~0xBFFFF这部分内存空间,来直接访问显存,显卡在加电自检后会初始化为80 * 25模式,即屏幕上显示25行,每行80个字符,每屏总共2000个字符。

显卡通过ASCII编码来识别CPU传入显存的数据。

屏幕上每个字符对应着显存中的连续两个字节,前一个字节为字符的ASCII代码,后一个字节为字符的显示属性。

image-20220122205323064

image-20220122205336604

image-20220122204330102

注意:Intel的处理器不允许把一个立即数传送到段寄存器,必须通过其他寄存器来中转。

mov指令

mov 目的操作数, 源操作数

注意:mov指令不允许目的操作数和源操作数都为内存单元,并且目的操作数不能为立即数。

注意:内存的增长是从低地址到高地址的。


除法

无符号除法指令:div

  • 16位除法:

    被除数放在ax寄存器中。

    格式:

    div x ;这里的x可以代表寄存器或者[内存单元]或者立即数
    

    \[ax / x = s \cdots m \]

    参数:

    • x:除数。
    • s:表示商,放在al寄存器。
    • m:表示余数,放在ah寄存器。
  • 32位除法:

    被除数的高16位放在dx寄存器中,低16位放在ax中。

    格式:

    div x ;这里的x可以代表寄存器或者[内存单元]或者立即数
    

    \[(dx << 4 + ax) / x = s \cdots m \]

    参数:

    • x:除数。
    • s:表示商,放在ax寄存器。
    • m:表示余数,放在dx寄存器。

在段之间批量传送数据

使用movsb和movsw指令可以在两个段之间批量传送数据。movsb每次传送一个字节,movsw每次传送一个字。

格式:

; 重复执行
rep movsb ; rep指令前缀表示cx寄存器的值不为0,则重复执行该指令。
rep movsw
; 只执行一次
movsb
movsw

参数:

  • 源地址:DS:SI,即段地址由DS寄存器提供,SI寄存器提供偏移地址。
  • 目的地址:ES:DI,即段地址由ES寄存器提供,DI寄存器提供偏移地址。
  • 数量:由CX寄存器指定。

传送方向:

  • 正向:从低地址到高地址,每次使SI和DI加1或2。
  • 反向:从高地址到低地址,每次使SI和DI减1或2。

一个特殊的寄存器flag

通过cld指令来将flag寄存器的DF位设置为0,std指令将flag寄存器的DF位设置位1。

ZF位表示运算结果是否为0。

  • 1:运算结果为0
  • 0:运算结果不为0

DF位表示movsb/movsw指令是正向还是反向。

  • 0:正向
  • 1:反向

image-20220123204058429


关于使用寄存器提供偏移地址的问题

在8086处理器上只能使用BX、SI、DI、BP寄存器来提供偏移地址。


加一和减一、加法和减法

加一和减一对应的指令为incdec

加法和减法对应的指令为addsub


负数相关指令

  • neg 寄存器:将对应寄存器的值变为负数。
  • cbw:无操作数,将AL寄存器的数值扩展到AX寄存器。
  • cwd:无操作数,将AX寄存器的数值扩展到DX和AX寄存器中,DX存高16位,AX存低16位。

jns指令

标志寄存器的SF位:当计算结果的最高位是0时,该位为0,否则为1。

jns指令,当标志寄存器的SF位为0时,则进行跳转。

标记寄存器的其他标志位

  • 奇偶标志位PF:当运算结果的低8位中,有偶数个1则PF为1,否则为0。
  • 进位标志位CF:当进行算术运算时,如果有向最高位进位或借位,则CF为1,否则为0。
  • 溢出标志位OF:如果发生溢出则为1,否则为0。

指令对标志寄存器的影响:

image-20220123215129845

条件跳转指令

根据标志跳转:

  • jz:ZF为1则转移,jnz:ZF为0则转移。
  • jo:OF为1则转移,jno:OF为0则转移。
  • jc:CF为1则转移,jnc:CF为0则转移。
  • jp:PF为1则转移,jnp:PF为0则转移。

根据比较结果跳转:

可以通过cmp指令进行比较。

image-20220123215659834

image-20220123215722000

根据CX寄存器进行跳转:

jcxz:如果CX寄存器为0,则进行跳转。


栈段

由SS寄存器来确定段地址,SP寄存器确定偏移地址。

压栈:push指令,会将SP的内容减去操作数的字长,再把操作数压入栈中。

弹栈:pop指令,会将SP的内容加上操作数的字长,再把栈顶的数据弹出。

注意:实模式下,栈的大小为64KB。


寻址方式

  • 寄存器寻址

  • 立即寻址

  • 内存寻址

    • 直接寻址

    • 基址寻址:使用BX和BP寄存器进行寻址

      注意:BP寄存器默认使用SS段寄存器,使得栈内数据可以像数据段一样访问。

    • 变址寻址:使用SI和DI寄存器进行寻址

    • 基址变址寄存器:同时使用BX/BP和SI/DI寄存器进行寻址


I/O端口读写方式

I/O端口的范围:0 ~ 65535(0x0 ~ 0xffff)。

  • 读取I/O端口

    in 存放位置, 端口号
    

    存放位置:必须是AL或AX寄存器。

    端口号:立即数或DX寄存器。

  • 写入I/O端口

    out 端口号, 写入数据
    

    端口号:立即数或DX寄存器。

    写入数据:必须是AL或AX寄存器。

一些磁盘相关的知识:

  • LBA28逻辑扇区编制法:即通过28个bit来表示逻辑扇区号,而不关心扇区的具体位置。(从0开始算起)

  • 端口号:

    端口号 含义
    1f2 表示要读取的扇区数
    1f3 表示逻辑扇区号的0~7位
    1f4 表示逻辑扇区号的8~15位
    1f5 表示逻辑扇区号的16~23位
    1f6 低4位存放逻辑扇区号的24~27位,第4位表示硬盘号(0主盘,1从盘),第6位表示扇区编制方式(0:CHS、1:LBA)
    1f7 命令端口:写入0x20表示读硬盘请求,状态端口:第7位表示硬盘忙碌状态(0不忙,1忙碌),第3位表示硬盘数据是否准备好(1:ok,0:no),第0位表示读取数据是否出错(1出错,0没出错)
    1f0 硬盘接口的数据端口(16位端口),当硬盘准备好后,可以从这个端口读取数据

    image-20220124155733782

image-20220124160018268


过程调用

call指令:

近调用的原理是先把IP寄存器的值压入栈中,再修改IP寄存器,而远调用则是分别把CS和IP寄存器的值压入栈,再修改CS和IP寄存器的值。

  • 直接近调用

    call 立即数
    
  • 间接近调用

    call 寄存器/[内存地址]
    
  • 直接远调用

    call 段地址:偏移地址
    
  • 间接远调用

    call far [内存地址]
    

    这种方式会从对应的内存地址中获取两个字的数据,前一个字作为段地址,后一个字作为偏移地址。

返回指令:

ret的原理是弹出栈中的值到IP寄存器,retf的原理是先弹出到IP寄存器,再弹到CS寄存器。

  • ret:相对近返回
  • retf:相对远返回

abc、shr、ror指令

  • abc:同add指令差不多,但是会加上CF位的值(CF是表示是否进位)。
  • shr:逻辑右移,类似C语言中的>>
  • shl:逻辑左移,类似C语言中的<<
  • ror:循环右移,将移出的bit放到左边空缺的位置,同时送入CF标志位。
  • rol:循环左移,将移出的bit放到右边空缺的位置,同时送入CF标志位。

image-20220124172321064


无条件跳转指令

  • 相对短转移:立即数表示偏移量

    jmp short 立即数
    
  • 相对近转移:立即数表示偏移量

    jmp near 立即数
    ; 或
    jmp 立即数
    
  • 间接绝对近转移:寄存器或内存地址表示偏移地址

    jmp near 寄存器/[内存地址]
    ; 或
    jmp 寄存器/[内存地址]
    
  • 直接绝对远转移

    jmp 段地址:偏移地址
    
  • 间接绝对远转移

    jmp far 寄存器/[内存地址]
    

伪指令resb

  • resb

    从当前位置开始,保留指定数量的字节,但不初始化它们的值。(区分db、dw、dd、dq)

  • resw:与resb相同,但是单位为字。

  • resd:与resb相同,但是单位为双字。


光标操作

屏幕上光标的位置保存在显卡内部的两个光标寄存器(8位)中,合起来是一个16位数值,表示在第几个位置(\(0 \sim {(80 * 25) - 1}\))。

注意:文字模式下,屏幕显示80*25个字符。

读取光标:

步骤:

  1. 向端口号0x3d4的索引寄存器指定索引,0xe是光标位置的高8位,0xf是光标位置的低8位。
  2. 通过端口号0x3d5的数据寄存器读取数据。

设置光标:

步骤:

  1. 向端口号0x3d4的索引寄存器写入光标位置的索引。
  2. 向端口号0x3d5的数据寄存器写入光标位置。

乘法

mul指令

mul 寄存器/[内存地址]
  • 8位乘法:将mul指令里指定的值乘以AL寄存器中的值,并把结果保存到AX寄存器中。
  • 16位乘法:将mul指令里指定的值乘以AX寄存器中的值,并把结果的高16位保存到DX,低16位保存到AX中。

cpuid指令

用于返回处理器的标识和特性信息。

在eax寄存器中指定要返回CPU信息。返回结果放在eax、ebx、ecx或edx中。


eflags寄存器

image-20220126165237566


comvcc指令

可以看成mov指令加上条件判断功能,与cmptest指令配合使用。

伪代码:

if (condition) {
    mov dest, source
}

sgdt指令

格式:sgdt 寄存器/[内存单元]

把gdtr寄存器的内容保存到指定位置。


movzx/movsx指令

  • movzx:带零扩展传送指令

    格式:movzx r16/r32, r8/r16/m8/m16

    把指定8位/16位寄存器/内存单元的数据放到16/32位寄存器中,并且将高位设置为0。

  • movsx:带符号扩展传送指令

    格式:movsx r16/r32, r8/r16/m8/m16

    把指定8位/16位寄存器/内存单元的数据放到16/32位寄存器中,并且将高位设置为跟符号位一致。


cmps指令

cmp指令的升级版,通过cx(16位)或ecx(32位)寄存器来指定比较次数,ds:si/esi寄存器指定源地址,es:di/edi寄存器指定目的地址,并根据eflags寄存器中的df位来决定地址变化的方向,\(df=0\)则正向比较,地址递增,\(df=1\)则反向比较,地址递减,如果没有加上rep指令前缀,则只比较一次。

cmpsb ;字节比较
cmpsw ;字比较
cmpsd ;双字比较

有关的rep指令前缀:

  • rep:一直重复到cx寄存器的值为0。
  • repz/repe:一直重复到cx寄存器的值为0或比较的内容不相等。
  • repnz/repne:一直重复到cx寄存器的值为0或比较的内容相等。

image-20220126173106954


调用门

调用门(Call Gate)用于不同特权级的程序之间进行控制转移。本质上只是一个描述符(不同于代码段和数据段)。

image-20220126200038307

调用门的特权级检查规则:

image-20220126203513197


pushf/popf指令

把16位的flags寄存器/32位的eflags寄存器压或弹栈到flag寄存器中。


GDT、LDT和TSS的关系

image-20220126204826235


任务切换与特权级切换的区别

image-20220126205808314


任务门

image-20220126210543989

image-20220126211109269

注意:任务是不可重入的。

任务切换:

  • 中断引起:

    在保护模式下,产生中断后CPU会根据中断号查询中断描述符表,如果对应的中断描述符是一个任务门,那么就会进行任务切换。

  • 使用远过程调用指令引起:

    在使用远过程调用指令时,CPU会去GDT中查找对应的段描述符,如果该描述符为任务门描述符,则会发起任务切换。


页目录项和页表项的结构

image-20220127171112427

参数解释:

参数 含义
P 该页表/页是否存在于内存中,1存在,0不存在
RW 读写位,0该页只读,1可读可写
US 用户/管理位,1允许所有特权级访问,0只允许特权级为0、1、2的程序访问
PWT 页级通写位,与高速缓存相关
PCD 页级高速缓存禁止位
A 访问位,表示该页是否被访问过
D 脏位,表示该页是否被写过
PAT 页属性表支持位,与高速缓存相关
G 全局位,表示该页是否为全局性的,如果是,则一直保留在高速缓存中
AVL 被处理器忽略,软件可使用

CR3寄存器的内容

image-20220127175857850


bts指令

将指定位置的bit设置为1,并将其旧值设置到eflags寄存器的CF位中。

格式:

bts r/m16, r16
bts r/m32, r32

保护模式下的中断和异常向量分配

在实模式下,是由中断向量表定义了中断的入口地址(位于内存最低端的1KB),而保护模式下,是由中断描述符表(IDT)定义了中断的入口。(由idtr寄存器指定了中断描述符表所在位置)

中断描述符表里面保存了中断门、陷阱门和任务门。

image-20220127203910140

中断门、陷阱门:

image-20220127204617586

中断描述符寄存器:

image-20220127204549991

保护模式下的中断处理过程:

image-20220127204822859

保护模式下通过中断实现任务切换:

image-20220127205126792


bound指令

用于检查数组是否超出索引(这个数组是指源操作数所指向的内存单元里面存放了上限和下限,大小为双字)

格式:

bound r16, m16
bound r32, m32

ud2指令

该指令无操作数,执行该指令会引发一个无效操作码异常。

格式:

ud2

标签:保护模式,x86,16,地址,指令,偏移,内存,从实,寄存器
来源: https://www.cnblogs.com/Lht1/p/15851258.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有