X86架构(四)——汇编指令与标志寄存器FLAGS

通过本节我们将了解:

  • 内存分段方式与访问
  • movsb、movsw、ins、dec、cld、std、div、neg、cbw、cwd、sub、idiv、jcxz、cmp等
  • INTEL8086标志寄存器

段地址的初始化

X86架构(三)汇编指令与显卡控制中有这么一条语句可能没讲清楚

mov [0x7c00+number+0x00], dl

该段程序是在编译后存储在主引导扇区的,所以默认的段:偏移地址为0x0000:0x0000所以段偏移是以0x0000计算的,如下图所示。第一条指令的偏移地址为0x0000,对应图中最左列。当这段程序被加载到0x0000:0x7c00开始执行后,因指令是由地址低到高顺序执行的,所以指令的偏移地址并不影响指令的执行,但是当我们要访问定义在后方的数据时,假设数据的偏移地址为0x012e(在硬盘中),当加载到0x0000:0x7c00后,数据的地址就变成了 0x012e+0x7c00了,这就是为什么要用0x7c00+number来定位数据地址的原因。这下应该讲清楚了,可以查看上一节进行确认哦!!!
lst文件
换句话说,编译后的汇编语言源程序将从某个内存段中,偏移地址为0 的地方开始加载。如果有一个标号label_a,它在编译时计算的汇编地址是0x05,当程序被加载到内存后,它在段内的偏移地址仍然是0x05,任何使用这个标号来访问内存的指令都不会产生问题。但是,如果程序加载时,不是从段内偏移地址为0 的地方开始的,而是0x7c00,label_a的实际偏移地址就是0x7c05。这时,所有访问label_a的指令仍然会访问偏移地址0x05,这样就会出问题,所以需要加上0x7c00。

逻辑地址0x0000:0x7c00对应的物理地址是0x07c00,该地址又是段0x07c0的起始地址。所以,我们可以把这512字节的区域看成一个单独的段,段的基地址是0x07c0,段长512 字节。这也就是前面说的一个物理地址可以对应多个逻辑地址。
多个逻辑地址
0x07c0:0x0000逻辑地址的划分下,前面的那条指令可以简化成

mov [number], dl

8086标志寄存器

8086处理器的标志寄存器如下图所示
8086处理器的标志寄存器
其中:

  • OF —— 溢出标志位
    OF 标志用于指示两个有符号数的运算结果是否错误
mov ah,0x70
add ah, ah
;从无符号数的角度来看0x70+0x70 < 0xFF, 结果正确
;从有符号数的角度来看0x70+0x70,理论上的计算结果超出了寄存器
;AH所能容纳的有符号数的范围-128127,所以破坏了符号位,故OF=1
  • DF —— 方向标志位
    该标志位用于改变处理器的运行状态。通过将这一位清零或者置1,可控制movsbmovsw的传送方向。
    mosb/movsw的说明参见后文
  • IF —— 中断允许标志位
    用于控制是否响应外部中断请求,置1时响应,反正不响应
  • TF —— 跟踪标志位
    为调试程序位而设定的陷阱控制位,当该位置1时,CPU处于单步执行状态,每执行一条指令产生一次内部中断,当该位清零后,CPU恢复正常工作。
  • SF —— 符号标志位
    处理器执行的很多算术逻辑运算都会影响到该位,如dec指令,如果计算结果的最高位是0,处理器把SF位置0,否则SF位置1。
  • ZF —— 零标志位
    当处理器执行一条算术或者逻辑运算指令后,算术逻辑部件送出的结果除了送到目的操作数指定的位置外,还送到一个或非门。或非门的输入全为0 时,输出为1;输入不全为0,输出为0。或非门的输出送到标志寄存器的ZF位。故计算结果为0,ZF位被置成1,表示计算结果为零为TRUE
  • AF —— 辅助进位标志位
    当执行加法或减法运算,使结果的低4位向高4位有进位或借位时,AF=1;否则AF=0
  • PF —— 奇偶标志位
    算术逻辑运算的结果的低8位中如果偶数个为1的比特,则PF=1;否则PF=0
  • CF —— 进位标志位
    当处理器进行算术操作时,如果最高位有向前进位或借位的情况发生,则CF=1;否则CF=0。
mov al 0x80
add al, al ; 执行后产生进位,CF = 1

NOTE 这些标志位常结合汇编指令一起使用,如下图所示
指令与标志

8086通用寄存器的特殊用法

  • BX 基址寄存器(Base Address Regiser)
mov [bx], dl ;将dl中的内容复制到以[bx]的内容为偏移地址的内存中
; 此种用法`仅适用于bx寄存器`!!!
  • AX 累加器(Accumulator)
  • CX 计数器(Counter)
  • DX 数据寄存器(Data)
  • SI 源索引寄存器(Source Index)
  • DI 目标索引寄存器(Destination Index)
    SI和DI也叫变址寄存器
    故8086支持基址+变址寄存器寻址
;INTEL8086 处理器只允许以下几种基址寄存器和变址寄存器的组合
[bx+si]
[bx+di]
[bp+si]
[bp+di]

汇编指令

  • movsb
  • movsw
    数据搬运指令
    用于把数据从内存中的一个地方批量地传送(复制)到另一个地方,movsb以字节为单位,movsw以字为单位。原始数据串的段地址由DS指定,偏移地址由SI指定,简写为DS:SI;要传送到的目的地址由ES:DI指定;传送的字节数或者字数由CX指定。传送分为正向传送反向传送
    正向传送是指传送操作的方向是从内存区域的低地址端到高地址端;反向传送则正好相反。正向传送时,每传送一个字节或者一个字,SIDI加1 或者加2;反向传送时,每传送一个字节或者一个字时,SIDI减去1或者减去2。不管是正向传送还是反向传送,也不管每次传送的是字节还是字,每传送一次,CX的内容自动减一。
    TIP 不妨理解为数组正向遍历与反向遍历
    NOTE DS为数据段寄存器;ES为附加段寄存器;CX、SI、DI为通用寄存器
    cld 		   ;`DF`标志清零,正向传送
	mov ds, 0x8000
	mov si, 0x0000 ;源地址 0x8000:0x0000
	mov es, 0x4000
	mov di, 0x0000 ;目的地址 0x4000:0x0000		                
    mov cx, 2
    movsw          ;将字数据从0x8000:0x0000移动到0x4000:0x0000
  • cld
    方向标志清零指令
    cld指令用于将DF标志清零,以指示movsb/movsw传送是正向的
  • std
    方向标志置位指令
    std指令用于将DF标志置1,以指示movsb/movsw传送是反向的
  • rep
    指令反复执行前缀,与ZF标志位相关,当ZF标志位为零(计算结果不为0)时,重复执行莫条指令
rep movsw ;CX不为零重复执行movsw
;因为 movsw没执行一次,CX的内容自动减一,而CX减一后的结果影响ZF标志位
  • loop
    循环指令
    用于重复执行一段相同的代码
loop digit ;转移到digit标号执行

rep相同,每执行一次loop指令,CX的内容减1;当CX为0(ZF为1)时,不转移到指定位置执行,继续执行后面的指令。

  • inc
    加一指令
    操作数可以是8位或者16位的寄存器,也可以是字节或者字内存单元
inc al
inc byte [bx]
inc word [labe_a]
  • dec
    减一指令
    操作数可以是8位或者16位的寄存器,也可以是字节或者字内存单元
dec al
dec byte [bx]
dec word [labe_a]
  • jns
    条件转移指令
    当标志寄存器的SF位不为1时跳转,jns是不是 jmp not signed(SF != 1)呢?可以是吧
  • NASM编译器的$$$标记
    $标记等同于标号,是一个隐藏在当前行行首的标号。因此,jmp near $的意思是,转移到当前指令继续执行.可以理解为while(1)
    $$标记,代表当前汇编节(段)的起始汇编地址
  • jz与jnz
    转移指令
    jz - ZF = 0 转移
    jnz - ZF != 0 转移
    __NOTE__关于ZF(零标志位)的说明请参见前文
  • jo与jno
    转移指令
    jo - OF = 0 转移
    jno - OF != 0 转移
    __NOTE__关于OF(溢出标志位)的说明请参见前文
  • jc与jnc
    转移指令
    jc - CF = 0 转移
    jnc - CF != 0 转移
    __NOTE__关于CF(进位标志位)的说明请参见前文
  • jp与jnp
    转移指令
    jp - PF = 0 转移
    jnp - PF != 0 转移
    __NOTE__关于PF(奇偶标志位)的说明请参见前文
    Attention
    转移指令必须出现在影响标志的指令之后,如
dec si ;dec指令影响SF寄存器
jns show
  • cmp
    比较指令
    cmp dst, src
    dst - 8位或者16位通用寄存器,8位或者16位内存单元
    src - 与目的操作数宽度一致的通用寄存器、内存单元或者立即数
cmp al, 0x08
cmp dx, bx

NOTE cmp指令仅仅根据计算的结果设置相应的标志位,而不保留计算结果
cmp指令将会影响到CF、OF、SF、ZF、AF 和PF 标志位
目的操作数是被测量的对象,源操作数是测量的基准,各种比较结果后通常跟随相应的条件转移指令,如下图所示。
比较结果与条件转移指令
比较结果与条件转移指令
-jcxz
条件转移指令(jmp if cx is zero)
当CX 寄存器的内容为零时则转移

jcxz show ;如果CX寄存器的内容为零,则转移;否则不转移,继续往下执行。

代码示例

NOTE 代码取自《X86汇编语言:从实模式到保护模式》
该程序被存储在硬盘的主引导扇区(偏移地址以0x0000计算),被加载到0x07c0:0x0000开始执行

 ;代码清单6-1
         ;文件名:c06_mbr.asm
         ;文件说明:硬盘主引导扇区代码
         ;创建日期:2011-4-12 22:12 
      
         jmp near start
         
  mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
            'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
  number db 0,0,0,0,0
  
  start:
         mov ax,0x07c0      ;设置数据段基地址 
         mov ds,ax			;ds寄存器一般保存数据段基地址
         
         mov ax,0xb800      ;设置附加段基地址 
         mov es,ax		    ;这里附加段指向显存位置,存放在es寄存器中
         
         cld			    ;将方向标志位DF清零,以指示传送是负方向的
         ;movsw指令原始数据串需要存放在ds:si位置
         ;mytext位于ds指向的基地址中,因此只要把偏移mytext存入si寄存器即可
         ;目的地址为es:di
         mov si,mytext		                
         mov di,0		    ;当前es指示显存起始位置,因此只要把偏移0存入di即可
         ;计算movsw执行的次数
         mov cx,(number-mytext)/2       ;实际上等于13
         rep movsw						;一次传送一个字(两个字节)
         ;此时Label offset被移到显存
     
         ;得到标号所代表的偏移地址
         mov ax,number					;此代码目的旨在显示number的偏移地址
         
         ;计算各个数位
         mov bx,ax					   ;bx指向当前number偏移地址
         mov cx,5                      ;循环次数 
         mov si,10                     ;除数 
  digit: 
         xor dx,dx	    ;dx(被除数高16位)清零 被除数dx:ax
         div si			;除法
         mov [bx],dl    ;保存数位,将数据移动到bx内存指向的偏移的内存中
         inc bx 		;bx自加1,指向下一个内存单元
         loop digit     ;cx不等于0,继续执行digit起始地址的代码
         
         ;显示各个数位
         mov bx,number 
         mov si,4       ; si = 4,但是我们实际显示的位数是5位,所以jns就解释了啊                      
   show:
         mov al,[bx+si]	;从后往前显示
         add al,0x30    ;转为ASCII码
         mov ah,0x04    ;显示属性
         mov [es:di],ax ;es指向0xb800; di在前面movsw时已自增
         add di,2
         dec si
         jns show		;上一条指令符号位为SF=0(结果为非负)时跳转
         
         mov word [es:di],0x0744

         jmp near $		;无限循环
		;$当前指令偏移,$$当前代码段起始位置
		;512减去最后填充的0x550xaa,剩余510个字节内存长度
		;再减去($-$$)计算得到的所用内存长度,就是要填充的长度
  times 510-($-$$) db 0	
                   db 0x55,0xaa

要细心领悟哦,指令与指令、指令与标志位的关系哦,收藏起来有需要的时候来看哦,bro…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值