关于网站设计的新闻,做网站用框架,明星网站建设,工业设计招聘零、本章概要
指令寻址#xff1a;解决的是PC1的问题 数据寻址#xff1a;使用寄存器/内存/结合
基址寻址#xff1a;用于多道程序的并发执行 直接寻址#xff1a;call 0x12345678 变址寻址#xff1a;esi edi用于循环#xff0c;因为使用直接寻址需要一堆…零、本章概要
指令寻址解决的是PC1的问题 数据寻址使用寄存器/内存/结合
基址寻址用于多道程序的并发执行 直接寻址call 0x12345678 变址寻址esi edi用于循环因为使用直接寻址需要一堆地址很麻烦速度也慢 相对寻址地址针对的是相对于PC的位置在代码位置发生改变的情况下用于实现跳转 PSW就是EFLAGS
x86汇编 使用cmpjnz(等)实现跳转指令cmp会修改PSW的内容jnz指令执行的适合会根据psw来判断需不需要跳转 循环语句for(int i0;i10;i){sarr[i];}i后面紧跟cmp 函数调用底层实现 堆栈平衡函数调用前后esp和ebp的位置不变一个线程内的所有函数共用一个堆栈 x86的push和pop默认是按照4字节压栈/出栈的
esp指向的是栈顶的位置(有元素)
push eax①先sub esp,4 ②再mov [esp],eax
pop eax①先mov eax,[esp] ②再add esp,4
call eax①push eip ②jmp eax
retpop eip默认是ret 4 真实的函数堆栈以及函数调用(见C语言函数调用的汇编视角) CICS和RISC指令集的对比
一、指令格式 操作码做什么 地址码对谁做
当然停机指令特殊不需要地址码就是执行一个特殊的命令而已
Intel的CPU使用的是x86架构x86架构的指令集和AMD的指令集是不兼容的不能跨不同指令系统的机器执行苹果公司宣布要抛弃Intel的CPU转而开发自己的基于ARM架构的CPU这样手机和电脑就能通用了 了解即可
堆栈型计算机通过零地址指令来进行算术运算操作数是隐藏在栈里面的而不会显示地在栈中指明扫描到操作符就相当于扫描到了一个零地址指令会把操作数计算/弹出堆栈 所有指令访存次数都要加上取指令的那一次 注意指令字长的长度是可变的(但是得是比如1B的整数倍)机器字长和存储字长都是不可变的
如果指令字长超过机器字长的话就需要多次的取指令操作CPU可以根据操作码的类型来判断 我们所有的指令都是遵循操作码地址码这种格式只是地址码有可能为0只有操作码这种情况出现表示仅执行一次特殊的操作 二、扩展操作码指令格式
操作码全1用于区分是几地址指令
这里重点讨论的是n位操作码能表示2^n种对于3/2/1地址的操作 三地址15条0000-1110 A1 A2 A3
二地址(12条)1111 0000-1011 A1 A2
一地址(62条)1111 11 000000-111101 A1
零地址(32条)1111 11 11111 00000-11111
cpu分析指令的时候就根据前几位是1来判断他是几地址指令 三、指令寻址 PC“1”
指令系统采用定长指令字结构 定长指令可变长操作码
因为每一条指令的长度是确定的所以1即可处理下一条指令 指令执行的时候先读入一个字
这里我想给大家重点讲解一下
你CPU不是要从内存当中取指令吗因为CPU一次最多只能同时处理一个字的数据你就直接去指令所在的内存单元(在PC里存放地址)去取出一个字的内容但是啊这一个字不是都有效啊我CPU处理第一个字节的时候发现是55后面直接扔掉就是了然后根据55我就能知道这条指令的长度了也就能知道PC应该加几了然后可以继续去取下一条地址了 四、数据寻址 根据寻址特征和形式地址可以确定操作数的真实地址(EA) 了解即可不用看
立即寻址立即数#010
直接寻址call 0x12345678
间接寻址lea [0x123456]地址放在某个内存单元里面
寄存器寻址push ebpmov eax,ecx
寄存器间接寻址push [eax4] 基址寻址就是用于多道程序并发执行的 直接寻址的bug要用一堆地址 如何解决 esi edi就是变址寄存器用于实现循环rep stosw 直接寻址地址是几就是几你位置移动也是跳到2那里
如何解决这个问题 你移动代码对顺序执行的指令没啥影响但是跳转指令难绷啊
所以只要修改跳转指令的解释方式因为PC会自动1所以使用-4(补码)来指示当前的CPU情况下应该跳转到哪里去执行
转移指令使用的都是相对寻址 基址寻址整个代码在内存当中的浮动相对寻址一段代码在程序内部的浮动 比较(相减)和跳转是分开的
PSW就是我们熟悉的ELAPGS 五、x86汇编语言基础
指令格式是什么样的取决于你的CPU硬编码就是指令集里面的指令
55就是opcode66就是前缀CPU根据值来判断
55为啥是push ebp而不是push bp和当前CPU的模式有关x86的保护模式默认是32位
前缀指令分成4个组每个组有n个最多出现一个
定长指令:opcode确定了指令的长度就确定了
变长指令即使opcode确定了指令的长度依然无法确定
opcode决定了有没有ModR/M也决定了是不是定长指令
比如opcode是88那它后面一定根一个opcode 地址偏移和立即数 比如这种考题 为啥x86汇编语言不允许两个操作数都同时来自主存
1、指令长度太长了
2、访问2次主存太慢了吧。。。 x86intel的8086/80286 80386
一条指令由操作码和若干地址码组成 进行除法运算的时候要对被除数进行位扩展32-64(edx:eax) 商存入eax余数存入edx intel格式(Windows)ATT(LinuxUnix)格式(汇编格式) 六、C语言选择语句的汇编实现
在x86当中IP就是PC
和前面提到的一样我们这样写汇编代码但是其实最后变成机器码的时候他会给我们翻译成与PC有关的指令这样代码段即使移动位置当PC指向这条语句的时候也能够跳转到正确的地址(使用PC一个补码) 使用标号可以更方便程序员去编写汇编代码最后编译器要做的就是把这个标号改成对于的地址 条件转移指令通常与cmp指令配合食用 switch语句离散情况不生成大表和if...else效率一样连续的时候生成大表先比较数值-1超过第二大的数就直接跳到default否则直接根据edx*4func(函数首地址) 来去查大表直接得到跳转地址而不用像if..else那样进行多次的比较
只有特定的指令(比如运算指令和ALU有关本质上不就是加法器那些产生的符号位吗)才会修改PSW寄存器里面的标志位cmp指令本质上就是做了一个减法(但是结果不保存只改变符号位)产生了符号位然后条件转移指令再根据减法所产生的标志位(去PSW寄存器中获取)来决定要不要进行跳转 六、C语言循环语句的汇编实现
i后面紧跟的就是cmp一旦越界就跳出循环
for①先初始化第一次直接判断是否跳出循环
②循环主体i③cmp i ,cnt(没结束就往上跳是for循环的一大重要特征) 七、C语言函数调用的汇编视角 x86的push和pop默认是按照4字节压栈/出栈的
esp指向的是栈顶的位置(有元素)
push eax①先sub esp,4 ②再mov [esp],eax
pop eax①先mov eax,[esp] ②再add esp,4
call eax①push eip ②jmp eax
retpop eip默认是ret 4 真实的堆栈以及函数调用 所谓堆栈平衡就是函数调用前和调用后esp和ebp的位置保持不变 试试看能不能画到堆栈平衡
外平栈(默认采用的调用约定)cdcall(右左外平栈:add esp,8)
内平栈stdcall(右左内平栈:ret 8(相当于add esp,8)) 注意一个线程里面的所有函数都使用同一个堆栈
int main(){
fun(2,3); push 3 push 2 call fun(push eip jmp fun)
}
fun: 八、CICS和RISC指令集的对比 你RISC只能通过寄存器来访存寄存器可不得比CICS多吗