![]() |
中国DOS联盟-- 联合DOS 推动DOS 发展DOS --联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum |
| 游客 | 登录 | 注册 | 会员 | 搜索 | 中国DOS联盟 |
|
中国DOS联盟论坛 现在时间是 2026-08-07 16:57 |
共 48,041 主题排行 / 350,128 发帖 / 今日 3 篇 / 48,252 会员排行 |
| 网络日志(Blog) » 【Redtek】 个人网志(学习笔记) |
| 可打印版本 29,510 / 130 |
| 第76楼 redtek | 发表于 2006-12-29 22:32 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 转移指令
可以修改IP或同时修改CS和IP的指令统称为转移指令。 转移指令就是可以控制CPU执行内存中某处的代码的指令…… 短转移IP的修改范围: -128~127 近转移IP的修改范围: -32768~32767 8086的CPU转移指令分为以下几类: 无条件转移指令(JMP) 条件转移指令 循环指令(LOOP) 过程 中断 操作符 offset Debiug ……
offset操作符取得了标号start和s的偏移地址。 复制start指令到s0处 复制的过程……
JMP指令 段间转移、段内短转移、段内近转移 范围是指字节。 jmp short s 段内短转移 jmp near ptr 段内近转移 jmp far ptr s 段间转移 段间转移
机器码: EA0B01490B 是目的地址在指令中的存储顺序。 (存放次序->低地址:高地址)即逆序存放。 低地址16位,高地址16位。 高地址代表转移的段地址,低地址代表偏移量。 0B49:010B 目标转移地址在机器码中段地址与偏移地址是逆序存放,段与偏移地址中的高位与低位也是逆序存放~:) 看来编译器在将指令(不管是高级语言还是汇编),最终全是机器码。 只不过这个机器码,不同的机器码代表不同的指令,机器码后面跟着数据, 这个数据是前或后(带符号数)或是所赋的值,然后CPU执行到当前指令时将当前IP+当前指令的长度,就算出下一条指令的地址,然后将 ip指过去。 这个时候CPU再执行已取回的指令,然后执行。 当遇到要转移的指令时,算出要转移到哪里,然后再次修改IP的值指向转移的地址(无论它是8位还是16位还是32位-段间转移), 然后执行IP所指向的指令。 如果当前执行的指令不是转移指令,则执行完后就执行上一次取这次指令时已修改的IP的值,所以它直接执行就行了。 如果用批处理做一个极简单的编译器,那么一个是机器码(这是固定),机器码后面跟着不同的数据,需要编译器来计算它们前后的位移量。 理论上可以实现(至于批处理文件--文本文件无法直接存二进制数据), 但也不是没有办法~:) 且VBS可以向文件写入二进制数据,那么完全可以用VBS做编译器。 乱想…… 简单的说,段内转移,段间转移,近转移这些名词,实际上只不过不同的机器码后面跟着8位(段内近转移),它是带符号数,也就是表示这么多寻址能力,当然是近转移。在CPU看来,只不过是 机器码后面跟着一个8位地址。 而近转移,只不过就是一个机器码后面跟着一个16位的地址数据。 16位所能表示出来的带符号数也就那么多。机器还是一样。 而被MASM编译器喜欢的语法,什么JMP FAR PTR 的段间转移,看这指令太长,到了机器码那里还不是: 机器码,后面跟着32位的地址数据。 而这个地址数据还是按照INTEL喜欢的逆序存放,低位后面是高位。 而那个32位的转移地址也是一样,先低后高,不管它什么段地址:偏移量, 到了最后还是段*16+偏移量,所以那个高位(后面的)自然就是表示段,用低位表示偏移量。反正到了CPU那里最后还是物理地址,这些段和偏移量只不过是读着方便和编程方便,它是给我们方便而看的。 按照书上的解释再举一反三的拔开那些迷雾…… 寻找机器码,先看看 mov 指令的机器码与寄存器它们的表示: Debug分析如下:
得到 MOV AX 的机器码是 B8 MOV BX …… 是 BB MOV CX …… 是 B9 MOV DX …… 是 BA (这些将来被CPU执行的时候都是二进制0011..所表示的“高”、“低”的电讯号…(先瞎想) 假如要开发向BX寄存器赋 0123H (23存在低位,01存在高位),它们存储状态的表示是 2301H,先读高位再读低位是为了视觉上的方便。而Inter要表示的是低位内存地址存低位,高位内存地址存高位。 现在写 MOV CX 这一个命令的编译器。向CX寄存器赋值 0123H(10进制291)。……(略……) 调用Debug…… 理论上相当于 MOV AX,0123 理论上是这样,但Echo不能直接写入二进制到文件。什么语言能写,什么语言就能够实现。 或是 用Debug装入还是 mov ax,0123 转移地址在内存中的jmp指令的两种格式 从内存单元地址处开始存放着一个字,是转移的目的偏移地址。 1) jmp word ptr 内存单元地址(段内转移) 2) jmp dword ptr 内存单元地址(段间转移) Debug 对比指令
使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
使jmp指令执行后,CS:IP指向程序的第一条指令 假如内存 2000:1000 BE 00 06 00 00 00 ... 跟据下面指令(不运行代码)直接写出结果 (CS)=? IP=? 低位:高位,所以 CS=0006,IP=00BE JCZX指令 cx=0时退出,8位位移,相当于 if((cx)==0) jmp short 标号。 利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后将它的偏移地址存储在dx中。 运行前提交“安排”数据 Loop指令 (cx)=(cx)-1 if (cx) 不等于 0 ... (ip)=(ip)+8位有符号数位移。 相当于C表示的: (cx) -- ; Debug分析代码执行过程…… 写视频缓冲区(直接写屏)彩色字符串 运行视图 内存地址: B8000H~~BFFFFH 共32KB的空间,为80*25彩色字符模式的显示缓冲区。它分为8页。 低位:字符ASCII,高位:色值(RGB)二进制表示0~7个位的“开关”。 [ Last edited by redtek on 2006-12-30 at 01:32 PM ] 附件 1.GIF (6.08 KiB) |
|
| 第77楼 redtek | 发表于 2006-12-31 03:57 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 call和ret指令
ret指令用栈中的数据,修改ip的内容,从而实现近转移; retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移。 CPU执行ret指令时,进行下面两步操作: (ip)=((ss)*16+(sp)) (sp)=(sp)+2 CPU执行retf指令时:进行下面四步操作: (ip)=((ss)*16+(sp)) (sp)=(sp)+2 (cs)=((ss)*16+(sp)) (sp)=(sp)+2 CPU执行ret指令时,相当于进行: pop ip CPU执行retf指令时,相当于进行: pop ip pop cs ret retf使用
根据PUSH压栈次序对调发现: 当执行retf时,CPU处理的时候是先计算低位,再计算高位。 所以,压栈是先进后出,所以要先压高位再压低位,才可以保证retf执行时先恢复高位后恢复低位,否则出错(相返)。 实现从内存 1000:0000 处开始执行指令 Debug跟踪结果
call指令 CPU执行call指令时,进行两步操作: 将当前的IP或CS和IP压入栈中; 转移。 call不能实现短转移。call指令实现转移的方法和jmp指令的原理相同。 call 标号(将当前的IP压栈后,转到标号处执行指令) CPU执行此种格式的call指令时,进行如下操作: 1) (sp)=(sp-2) (ss)*16+(sp)=(ip) 2) (IP)=(IP)+16位位移 16位位移由编译程序在编译时算出 相当于 CPU 执行指令 "call 标号" 时进行: push IP jmp near ptr 标号 下面程序执行后,ax中的数值为多少? 答:执行过程如下: 1) CPU读入 mov ax,0 ,然后将 ip 指向下一条指令 2) 执行 mov ax,0 以后,读入当前ip所指向的指令(call s) 3) 读入 call s,然后将ip指向下一条指令 inc ax(它的IP是6) 3) 执行 call s 指令-》将当前IP值压栈(6),然后执行跳转……S标号…… 4) CPU读入指令 pop ax ,将ip指向下一条指令。 5) ……最后 AX=0006 表面上看上去不应该是6,而应该是当前 call s 所在的指令ip地址,但实际上按照CPU执行以及设置IP的次序,它依上述所示。 转移的目的地址在指令中的call指令 指令 "call far ptr 标号" 实现的是段间转移。 CPU执行此种格式的call指令时,进行如下操作: 1) (sp)=(sp)-2 先压高位 ((ss)*16+(sp))=(cs) (sp)=(sp)-2 ((ss)*16+(sp))=(IP) 再压低位 2) (CS)=标号所在段的段地址 (IP)=标号所在段的偏多地址 相当于: push CS push IP jmp far ptr 标号 分析 转移地址在寄存器中的call指令 指令格式: call 16位寄存器 功能: (sp)=(sp-2) ((ss)*16+(sp))=(IP) (IP)=(16位寄存器) CPU执行 call 16位reg时,相当于进行: push IP jmp 16位寄存器 实验 1) 压当前IP到栈(只压IP),压了ip是为了Call完以后回来时执行ip指向的地址指令 2) jmp 取指令16位寄存器中要求跳转的地址 转移地址在内存中的call指令 两种格式: 1) call word ptr 内存单元地址 2) call dword ptr 内存单元地址] 这两种格式都是先压入IP,然后取要跳转的指定内存单元地址。 call word ptr ... call dword ptr 内存单元地址 dword是32位,所以所取的内存单元地址中足够“包含”(段:偏移量)地址。 但CPU处理次序是先低位再高位,所以必须先压(段)再压(偏移:就是IP),当CPU处理时才可以先处理低地址再高地址 所以,dword内存单元中(双字)排列次序:低位:高位 在sp中存放的是ip地址(16位)(字) 当CPU执行 call dword ptr 内存单元地址时,相当于进行: push CS push IP jmp dword ptr 内存单元地址 即:栈内的数据存的是Call完以后回来时要运行的指令地址(也要保持将来先取到低位再取到高位) 而内存单元内存的数据(低位:高低)是指要 jmp dword ptr 的地址~:) 最后 bx中的值是8 call和ret配合使用 当执行完“子程序段”时,由 ret 指令将call调用前压入栈中的IP再弹出来,这样就恢复的调用前的IP,程序继续执行~:) 关于call与ret mul乘法指令 两个相乘的数,要么都是8位,要么都是16位。 计算: 如果是8位,一个默认放在AH中,另一个放在8位寄存器或是内存字节单元中。 如果是16位,一个默认在AX中,另一个放在16位寄存器或内存字单元中。 结果: 如果是8位乘法,结果默认放在AL中。 如果是16位乘法,结果高位默认放在DX中,低位在AX中存放。 格式: mul reg 或 mul 内存单元 内存单地可以用不同的寻址方式给出: mul byte ptr ds: 含义:(ax)=(dl)*((ds)*16+0) mul word ptr 含义:(ax)=((ax)*(ds)*16+(bx)+(si)+8) 结果的低16位 (dx)=((ax)*(ds)*16+(bx)+(si)+8) 结果的高16位 计算 100*10000 模块化程序设计 参数和结果传递的问题 计算data段中第一组数据的3次方,结果保存在后面一组dword单元中 结果
批量数据传送 将批量数据放到内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序。 即,相当于是按址传送数据。刚才上面的寄存器传送最终结果,相当于按值似递。在c、vb中均有类似的说明~:0 将data段中的字符串转为大写 Debug 过程
编写子程序注意的问题: 子程序开始: 子程序中使用的寄存器入栈 子程序内容 子程序中使用的寄存器出栈 返回(ret,retf) 子程序例程:用子程序转所有字母为大写 上面子程序内,使用栈先保存当前寄存器状态,然后再执行子程序内部计算过程,计算完后再弹出保存的值,不与调用者冲突。
编写子程序 要求:在指定的位置,用指定的颜色,显示一个用0结束的字符串。 dh=行号(取值0~24),dl=列号(取值范围0~79) cl=颜色, ds:si指向字符串的首地址 在屏幕的8行3列,用绿色显示 data 段中的字符串。 上面显示方式是直接写显存(直接写屏)方式,在CMD下运行编译的.exe无效,Debug内运行有效。MS-DOS 环境下直接运行编译的.exe文件有效。 [ Last edited by redtek on 2007-1-1 at 02:02 PM ] 附件 1.GIF (11.55 KiB) |
|
| 第78楼 redtek | 发表于 2007-01-02 04:04 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 解决除法溢出问题
例如下面代码将会溢出:(1000 / 1) 子程序描述: 名称: divdw 功能: 进行不会产生溢出的除法运算。被除数为dword型,除数为word型,结果为dword型。 下面公式将可能产生溢出的除法运算,转变为多个不会产生溢出的除法运算。 公式: X/N=int(H/N)*6636 + /N] 商:(被除数高16位 / 除数) * 65536 + / 除数 返回:(dx)=结果的高16位 (ax)=结果的低16位 (cx)=余数 根据公式写出子程序以及完整代码: 结果 [ Last edited by redtek on 2007-1-3 at 01:46 PM ] |
|
| 第79楼 220110 | 发表于 2007-01-02 12:09 |
| 荣誉版主 发帖 313 积分 718 | |
|
推荐你看<<黑客反调试技术>>.
PS:这不是一本黑客技术的书,着重是反调试技术.从正常软件生产来看掌握调试技术是必要的,但从不道德的角度,反调试就是破解,反编译.这书有相当的难度. |
|
| 第80楼 redtek | 发表于 2007-01-04 02:37 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
非常感谢220110版主推荐~:)
在当当网找到一本《黑客反汇编揭秘》,不知道是不是这本书? 还有一本叫做《黑客调试技术揭秘》,作者:(美)卡斯帕克尔。 不知道《黑客反调试技术揭秘》和《黑客调试技术揭秘》这两本是不是指同一本书? 《黑客调试技术揭秘》的目录: [ Last edited by redtek on 2007-1-3 at 01:43 PM ] |
|
| 第81楼 redtek | 发表于 2007-01-04 03:22 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 子程序
数值显示 将数据用十进制形式显示到屏幕上,进行两步工作: 1) 将二进制信息存储的数据转变为十进制形式的字符串; 2) 显示十进制表式的字符串 以数字表示的16进制(如 1 , 2, 3, ... )与数字的ASCII码之间相差30H,即1H:如果希望显示1,则必须1H+30H才等于它的ASCII码。 编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。 因为使用了要显示的数字除以10,做为分解的每一个位余数+30H就等于要显示的这个数字的ASCII,所以出来的是倒序了。 (所以相代码未完,需要显示前倒序一次就变成可以显示的正确的数字了) 这回可以正序显示了~:) [ Last edited by redtek on 2007-1-4 at 04:53 PM ] |
|
| 第82楼 amao | 发表于 2007-01-04 04:08 |
| 中级用户 发帖 152 积分 316 | |
|
对我来说有如看天书啊。
|
|
| 第83楼 redtek | 发表于 2007-01-05 03:09 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
哈哈……
我每往后学一点儿都像看天书一样~:) 不过难就难吧,一天一点儿,总有掌握它的这一天~:) |
|
| 第84楼 redtek | 发表于 2007-01-05 07:10 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 实验 寻址方式在结构化数据访问中的应用
代码如下: 运行结果 [ Last edited by redtek on 2007-1-5 at 04:28 PM ] |
|
| 第85楼 9527 | 发表于 2007-01-06 00:27 |
| 银牌会员 发帖 438 积分 1,185 来自 北京 | |
|
redtek兄,我也想学学,能把你现在看的书名或者电子书还有网站告诉我吗?期待中ing........
|
|
| 第86楼 redtek | 发表于 2007-01-06 03:52 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
因为书名相同但出版社不同的书很多,所以列出在当当网可以查到的详细关于这本书的介绍信息~:)
(下面这本书如果是自学,极难。整本都是望而生畏的内容,很可能看几眼就再也不想学汇编了。) (但其内容非常详细,教学方式适合学校有专业老师辅导。) (但用某些汇编快速入门的书自学一段时间后,还必须要看这本书,因其内容很全) Intel 80X86/Pentium汇编语言程序设计(第二版) http://product.dangdang.com/product.aspx?product_id=8936113 (下面这本书适合在没有任何人帮助你的情况下,完全自学汇编语言) (很少有望而生畏的内容--因全是入门内容) (不适合学校教学之用--因为适合完全自学,所以此书教学速度极慢) 汇编语言 http://product.dangdang.com/product.aspx?product_id=8732001 (下面这本书内容非常深,从最简单的十几句汇编语言写一个极微小的“操作系统”到完成一个操作系统的每一步底层) (看完这本书就比较精通操作系统底层并可以自己写较简单的操作系统了……) (要求熟悉汇编与C语言--但主要以C语言介绍如何写操作系统) 自己动手写操作系统 http://product.dangdang.com/product.aspx?product_id=9024751 (希望精通磁盘和准备自己从头到尾的写一个Linux操作系统出来,看这本书) Linux 0.01内核分析与操作系统设计(附CD—ROM光盘一张)——创作你自己的操作系统 http://product.dangdang.com/product.aspx?product_id=8956136 建议不要找电子版的书。电子版的书不适合出门带着看、爬在床上看、坐在椅子上在任何地方看、随时翻着看…… 其它资料就是lxmxn在这个网志里介绍的那个网站和Google.com上可以找到的网站~:) 一定要花钱买,强迫自己要不就学出来!要不就白花钱惩罚自己~:) 如果不强迫自己花钱买,很可能遇到难的地方就主动放弃了~:) |
|
| 第87楼 9527 | 发表于 2007-01-06 04:11 |
| 银牌会员 发帖 438 积分 1,185 来自 北京 | |
|
谢谢redtek兄提供学习资料,是啊,有时候不给自己一点压力是不成啦,会变的很懒散很颓废很是没用啊
|
|
| 第88楼 redtek | 发表于 2007-01-06 04:17 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 标置寄存器
ZF标志 flag的第6位,记录相关指令执行后其结果是否为0。0-ZF=1 1-ZF=0 add,mul,div,inc,or,and等运算指令,都会影响标志寄存器。 mov,push,pop等指令对标志寄存器没有影响。 PF标志 flag的第2位是PF,奇偶标志位。记录相关指令执行后,结果的所有二进制位中1的个数是否为偶数,偶数:PF=1,奇数:PF=0 SF标志 flag的第7位是SF,符号标志位。记录相关指令执行后,结果是否为负。负:SF=1,非负:SF=0 它是对有符号数运算结果的一种记录,它记录数据的正负。对于无符号数据计算,SF的值没有意义。 FLAG标置位寄存器结构: 在8086 CPU中flag的1、3、5、12、13、14、15没有使用。 在Debug中是 “NV UP EI PL NZ NA PO NC” 次序出现的。 其标置为1还是为0,在Debug中是以其它字母缩写表示的。为1或是为0表示的字母都不一样。 例如:第6位ZF标志(是否为0。结果为零=1,结果非零=0) 在 Debug 中表示其“原始”状态,即均为0时的状态:
NZ就是CF标置的当前表示状态。 至于NZ为什么要出现在从右数向左数的第6位(表面上看是第4位),因为是从FLAG寄存器的低位0表示。 而1、3、5位在8086CPU中未使用,所以“空”过它们,ZF标志自然就出现在了表面上的第4位(实际第6位)。 ZF:零标置位。 当结果为0时,ZF=1,用ZR表示(出现在Debug中)。 当结果为1时,ZF=0,用NZ表示。 记忆其特点: 当看到ZR时,表示结果为0,ZF零标置位当结果为0时为真,所以ZF=1。 当看到NZ时,表示结果非0,ZF零标置位当结果非0时为伪,所以ZF=0。 写出下面每条指令执行后,ZF、PF、SF等标志位的值 CF标志 flag的第0位是CF,进位标志位。 在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高的进位值,或从更高位的借位值。
OF标志 flag的第11位是OF,溢出标志位。 OF记录了有符号数运算的结果是否发生了溢出。溢出:OF=1,没有溢出:OF=0。 对于有符号数而言。 CPU在执行add等指令的时候,包含了两种含义: 1) 无符号数运算 CPU用CF位来记录是否产生了进位。 2) 有符号数运算 CPU用OF位来记录是否产生了溢出。 用SF位来记录结果的符号。 写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值。 DF标志和串传送指令 flag的第10位是DF,方向标志位。 在串处理指令中,控制每次操作后si,di的增减。 DF=0 每次操作后SI,DI递增。 DF=1 每次操作后SI,DI递减。 标置 值为1的标记 值为0的标记 ------------------------ DF DN UP 格式: movsb 功能: 执行movsb指令相当于进行下面几步操作。 1) ((es)*16+(di))=((ds)*16+(si)) 2) 如果DF=0则: (si)=(si)+1 (di)=(di)+1 如果DF=1则: (si)=(si)-1 (di)=(di)-1 movsb的功能是将ds:si指向内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值,将si和di递增或递减。 一般movsb(byte)和movsw(word)都和rep配合使用。 用汇编语言来描述 rep movsb 的功能: s: movsb loop s 8086 CPU 提供下面两条指令对DF位进行设置: cld 指令: 将标志寄存器的DF位置0 std 指令: 将标志寄存器的DF位置1 例程:将 data 段中的每一个字符串复制到它后面的空间中。 在 Debug 中 cld 与 rep movsb 指令为: 例程:用串传送指令,将F000H段中的最后16个字符复制到data段中。
编程:编写一个子程序,将包含任何字符,以0结尾的字符串中的小写字母变成大写字母。 名称: letterc 功能:同上 参数:ds:si 指向字符串首地址 需要进行转化的是字符串中的小写字母‘a’~‘z’,而不是其他字符。 ) 小写字母范围: 97 ~~ (97+26-1) 97 ~~ 122 ) 结束标置: 0 调试代码过程中发现一个有意思的错误:)
当 loop 在递减 CX 寄存器的值的时候发生的上面错误。 所以,代码中不使用 loop ... cx了,重新使用 jcxz 命令。 遇到一个问题: 在汇编编辑器中,"Beginner”的 'B' 打头字母无论如何更新总是小写方式,后发现在编辑器中 “格式设定” 小写,问题在这里。 将其设置成 “不处理” 即可首字母随意设置。 Debug 调试结果:
[ Last edited by redtek on 2007-1-8 at 02:45 PM ] |
|
| 第89楼 redtek | 发表于 2007-01-07 07:47 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 ADC指令
功能: 操作对象1 = 操作对象1 + 操作对象2 + CF adc是带位加法指令,它利用了CF位上记录的进位值。 指令 adc ax,bx 实现的功能是: (ax)=(ax)+(bx)+CF
adc指令和add指令相配合可以对更大的数据进行加法运算。 例:计算 1EF000H + 201000H,结果放在AX(高16位)和bx(低16位)中。
先低位与低位相加,结果保存在低位中。 当低位相加产生了进位时,其CF的值为1,Debug中表示:CY(代表进位状态) 然后 adc 指令:高位与高位相加,其相加结果由 adc 指令会自动再与CF相加,因刚刚有了进位,所以高位存放的是进位后的值。 高位与低位的组成就是计算在数相加的总值。 计算 1EF0001000H + 2010001EF0H ,结果放在AX(最高16位),BX(次高16位),CX(低16位)中。
两个1024位数字相加 sbb指令 sbb是带借位减法指令,它利用了CF位上记录的借位值。 功能:操作对象1 = 操作对象1 -操作对象2 -CF 例: sbb ax,bx 实现的功能是: (ax) = (ax) - (bx) - CF sbb指令执行后,将对CF进行设置。利用sbb指令可以对任意大的数据进行减法运算。 计算 003E1000H -00202000H,结果放在AX,BX中。
当执行 SUB BX,2000 时,就是(1000 -2000 )计算,低位中这个计算必然是负数,而且它需要借位。 所以,紫色 NG 符号标置表示这个计算结果是负数。 红色 CY 进位/借位标置表示。 计算时因为是借位,所以 CF=1,在执行比当前低位再高一点的位的时候,则sbb指令会两个数字相减后再减去CF的值,因为借位了,所以要减去。 pushf 和 popf 指令 pushf的功能是将标志寄存器的值压栈。 popf是从栈中弹出数据,送入标志寄存器中。 pushf和popf,为直接访问标志寄存器提供了一种方法。 经过分析,pushf压入栈内的标置寄存器的16位值并非与Debug上显示的位置相对应。 因为标置寄存器的第 1、3、5、12、13、14、15位在8086CPU都没有使用(不具有任何含义),所以压栈后的二进制位表示也是这样安排的。 在80586 CPU中标置寄存器的长度可达32位,有如天书一般。 [ Last edited by redtek on 2007-1-8 at 11:22 AM ] |
|
| 第90楼 redtek | 发表于 2007-01-07 23:01 |
| 金牌会员 发帖 1,147 积分 2,902 | |
|
【80x86汇编语言学习笔记】 cmp比较指令
cmp的功能相当于减法指令,只是不保存结果。 cmp指令执行后,将对标志寄存器产生影响。 其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。 例: cmp ax,ax 相当于: (ax) -(ax) ,结果为0,但并不在ax中保存。 仅影响 flag 的相关各位。 指令执行后: ZF=1 PF=1 SF=0 CF=0 OF=O
PE是奇偶标置位,cmp ax,bx 即(8 - 3)=5。 5的二进制表示:00000101 其中有2个1,也就是说1的出现次数是偶数,所以PF=1。 通过cmp(几乎每次都打成cmd)指令执行后,相关标志位的值可以看出比较结果。 CPU在执行cmp指令的时候,包含两种含义:进行无符号数运算和进行有符号数运算。 关于比较计算与补码的关系: 分析如下: AH=0A0H = -96(十进制) BH=0CBH = -53(十进制) CMP AH,BH 相当于减法计算: (AH)=(-96)-(-53) =(-96)+53 =-43 (十进制) 得到了-43,现在求有符号数表示(-43的补码): |-43| 绝对值为: 43 43=0010 1011 (二进制表示) =1101 0100 (将它求反) =1101 0100 + 1 (加1,防止重码) =1101 0101 (结果) =D5H (16进制结果:就是-43的补码) 所以,结果是负数,SF符号位标置为1(负数)。 过程
检测比较结果的条件转移指令 大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP。 被cmp指令影响的那些标置位,这些转移指令通常都和cmp相配合使用。 条件转移指令: je、jne、jb、jnb、ja、jna 上面条件转移指令所检测的标志位,都是cmp指令进行无符号数比较时候,记录比较结果的标志位。 例: je (等于),检测 ZF 位,当ZF=1的时候进行转移。如果在je前面使用了cmp指令,则je对ZF的检测实际上就是间接地检测cmp的比较结果是否为两数相等。 因为 cmp 比较实际上就是两个操作数相减,如果两个操作数值一样,自然相减为0,ZF(是否为0标置位)=1,代表为0。而 je 判断 ZF 标置位,所以就检测到了是相等。 je 检测的是ZF标置,不管 je 前面是什么指令,只要CPU执行JE指令时,ZF=1,那么就发生转移。 如果 ah与bh相等,则 cmp 会将 ZF的值置1,所以后面的 JE 指令判断 ZF=1,则跳转到指定位置。 虽然没有了 cmp 与 je 的配合,但是 add ax,0 就相当(AX)=(AX)+0 。ax=0,那么ax=ax+0还是0 ,结果为0所以 add 指令将ZF=1。当 je 指令执行时,它判断ZF=1的标置,如果发现ZF=1,则认为相等,进行跳转。 (即,不管是什么指令让CF=1的,只要je判断CF=1,就一定发生跳转) 很多种方法都可以让ZF=1,所以标置寄存器与cmp的原理必须掌握。对这些原理的理解有多少,就意味着对指令深度的理解和灵活开发。 CPU提供了cmp指令,也提供了je等条件转移指令。将它们配合使用,可以实现根据比较结果进行转移的功能。 对于je、jne、jb、jnb、ja、jna等指令和cmp指令配合的使用思和je相同。 例: 在 data 段中统计 8 出现的次数 出现了3次8。
例:统计 data 段中数值大于8的字节的个数,用 AX 保存统计结果。 例:统计 data 段中数值小于8的字节个数,用 ax 保存统计结果。 [ Last edited by redtek on 2007-1-7 at 08:19 PM ] |
|
| 上一页 1 … 3 4 5 6 7 8 9 下一页 |
|
[ 联系联盟系统管理团队 -
中国DOS联盟 -
标准版 ] Sponsored by ifanr Inc | © 2001–2023 |