当前位置: 首页 > article >正文

101 - Lecture 9

CPU的内部操作,包括寄存器、堆栈和指令执行过程

CPU Registers

CPU寄存器
• CPU寄存器是CPU内用于临时存储数据的特殊存储器。寄存器的操作速度比主存储器(内存)更快。Pentium处理器中各种寄存器,包括通用寄存器、基地址寄存器、指令指针(EIP)等。

CPU status flags

CPU状态标志(Flags)
• EFLAG寄存器保存了CPU的状态标志(status flags),状态标志是用于存储在算术运算等过程中产生的结果

•如:符号标志(Sign)、零标志(Zero)以及进位标志(Carry)等。

每个标志代表不同的运算状态,如溢出(overflow)、进位(carry bits)等,帮助CPU在不同指令之间传递信息。使得后续指令能够根据前一条指令的结果做出相应的处理。

请添加图片描述

– S: sign.符号位,表示运算结果的正负
– Z: zero, result being zero.
– C: carry, indicates an arithmetic carry.
– O: arithmetic overflow error

在二进制的补码系统(Two’s complement system)中,将两个大的正数相加可能会得到一个负数的结果。这听起来违反直觉,因为在常规的算术中,正数加正数总是得到正数。但在计算机的补码系统中,由于位数的限制和特殊的编码方式,当两个正数相加超过该系统能表示的最大正数时,会发生溢出,从而导致结果变成负数。

关联关键词解析:

  1. 补码系统:补码(Two’s complement)是一种用于表示有符号整数的方法

算术溢出错误(Arithmetic Overflow Error)

二进制数相加时发生溢出的情况。当两个数相加的结果超出了能够表示的位数范围时,产生溢出。触发了溢出标志(OF will be set)。

请添加图片描述

请添加图片描述

CPU标志位(CPU Flags)的操作。具体来说,它展示了CMP指令和JZ指令的操作:

•	CMP(比较):比较两个值,将结果通过减法(subtract)得出,但不会保存结果,只会设置标志位,比如零标志(Z flag)。
•	JZ(条件conditional跳转jump):根据Z标志位的状态进行条件跳转。如果Z标志被设置,说明两个数相等,JZ会执行跳转。

Inline Assembler(内联汇编):

• 内联汇编器:
我们会在Microsoft Visual C++环境下使用内联汇编器。所谓内联汇编就是在C/C++代码中直接插入汇编语言代码,使其能够在编译时与C/C++代码一起编译和执行。相比独立的汇编程序,内联汇编具有与C/C++语言无缝结合的优点

• 汇编代码(assembly code)引用(refer to)C/C++变量:
在内联汇编中,可以直接引用C/C++的变量。

例如: _asm mov eax,var

var是C/C++中的变量名,而汇编指令mov eax,var将该变量的值存入EAX寄存器中。

语法规则:内联汇编

1.可以使用_asm关键字来嵌入单行汇编代码。如果是多行汇编代码,可以使用带花括号的_asm{…}。
2. 汇编代码中的注释可以使用分号;,类似C/C++中的//注释。也可以在汇编代码中使用//来注释。

• 调用C函数:通过使用_asm{…}的语法,可以在汇编代码中调用C语言函数,包括标准的C库函数。这意味着我们可以在汇编代码中使用类似printf和scanf这样的C函数。C library routines

• 使用堆栈(stack)传递参数:当我们调用printf或其他类似函数时,参数是通过堆栈传递的。

堆栈是函数调用时保存局部变量和函数参数的重要数据结构。

printf 和 scanf

printf("%d\n", x);用于输出整数x,其中%d是格式说明符,表示输出整数值。

scanf("%d %s %d", &day, monthname, &year);
%d用于整数,%s用于字符串。读取的数据分别存储在day、monthname和year变量中。

• printf 和 scanf 通常结合使用,先用 printf 提示用户输入信息,然后使用 scanf 接受用户输入。

int x;
printf("Enter a value for x: ");
scanf("%d", &x);
printf("You entered: %d\n", x);

& 是 取地址符(address-of operator)。它的作用是返回变量在内存中的地址,而不是变量本身的值。我们可以通过这个符号获取一个变量的地址,并将这个地址传递给像 scanf 这样的函数,这样函数就可以直接操作存储在该地址上的数据

printf 用于输出数据,scanf 用于从用户输入中读取数据。

堆栈(Stack)

1. Stack的定义和特性:

Stack is a memory arrangement(data structure)
• 堆栈是一种内存排列方式(数据结构),用于存储和检索(retrieval)信息(通常是值或变量)。它的存储和检索顺序可以描述为LIFO(Last In, First Out),即后进先出(后进去的数据最先被取出来)。栈常用于函数调用、撤销操作等场景。

• ESP(栈指针寄存器)(stack pointer register)用于存储堆栈顶端项的地址。

• 队列(Queue)是与堆栈不同的另一种数据结构(memory arrangement),遵循FIFO(First In, First Out),即先进先出。

堆栈的基本操作:PUSH 和 POP:

• 每个堆栈都有两个基本操作:PUSH(压入)和POP(弹出)

• PUSH操作将一个值压入堆栈,POP操作从堆栈弹出一个值。

请添加图片描述

当数据被推入(push)堆栈时,ESP寄存器的值会减小,当数据从堆栈中弹出(pop)时,ESP寄存器的值会增加。ESP寄存器对于管理函数调用、变量存储和程序执行中的各种临时数据非常重要。

1. 堆栈在汇编语言中的实现:

• 堆栈是一个简单但非常有用的数据结构。几乎所有汇编语言都有专门的指令来实现堆栈操作。

• 在内联汇编(Inline Assembly)中,PUSH和POP操作使用栈指针寄存器ESP来存储栈顶元素的地址。
• 通过PUSH和POP指令,可以分别压入和弹出数据,并通过ESP寄存器管理堆栈。

2. 倒置堆栈的假设与PUSH和POP的具体工作原理:

•	这里假设堆栈在内存中是“倒置”(upside-down stack)的,意思是地址从高到低排列。

•	PUSH操作的具体步骤是:
1.	通过减少ESP中的地址,腾出堆栈空间。
2.	然后使用ESP中的地址将值写入堆栈。

•	POP操作的具体步骤是:
4.	使用ESP中的地址读取堆栈中的值。
5.	然后通过增加ESP中的地址来移除堆栈中的项。

请添加图片描述

• PUSH EAX:将EAX寄存器中的32位值压入堆栈。
• CALL print:执行一个子程序,该子程序与堆栈无关,但展示了堆栈在函数调用中的常见使用。
• POP EBX:从堆栈中弹出32位数据,并将其存储到EBX寄存器中。

Adjusting stack pointer

在编写程序时,程序员有时需要手动调整堆栈指针(stack pointer)。
堆栈指针指向堆栈顶部的数据项。在某些情况下,程序员可能需要从堆栈中移除数据项,或者为即将存入堆栈的数据预留空间。
例如,在汇编语言中,可以使用 “ADD ESP,4” 指令来从堆栈中移除(或者说“弹出”)4个字节的数据。这样做通常是为了清理堆栈上之前操作留下的数据。
“SUB ESP,256” 指令则是用来在堆栈上创建256字节的空间,这在需要存储大量临时数据时非常有用。

Stack as the temporary store

栈作为临时存储的功能,特别是如何在C语言中结合汇编语言来使用栈保存循环计数器和参数。

•	mov ecx, 10:将循环计数器ecx初始化为10。
•	push ecx:将当前循环计数器的值(10)压入栈中,保存起来。
•	lea eax, format:加载字符串地址到寄存器eax。
•	push eax:将字符串的地址压入栈中,作为printf函数的参数。
•	call printf:调用printf函数打印“Hello World”。
•	add esp, 4:清理栈,释放printf的参数所占用的空间。
•	pop ecx:从栈中恢复保存的循环计数器值到寄存器ecx。
•	loop Lj:减少ecx的值,如果ecx不为0,则跳转回标记Lj,重复上述操作。

这个程序展示了如何使用栈来暂时保存循环计数器ecx的值,以便ecx寄存器可以用于其他目的,同时在循环结束后恢复其值。

• 栈被用作“scratch-pad”(临时记事本)来存储循环计数器的值,以便释放寄存器(free ECX register)用于其他用途。
• 这是栈的常见使用场景,特别是在需要临时保存变量的场合,栈可以用于存储和恢复这些值。

Passing parameters

• 堆栈除了用于存储数据外,还可以用于传递函数参数。printf函数的参数通过堆栈传递,而使用ESP指令调整堆栈以清除参数。

printf是一个常见的用于格式化输出的函数,在C语言中广泛使用。在使用printf进行打印时,需要提供两个方面的信息:
一是要打印的内容
二是打印的格式
这些信息通常通过参数(parameters)的形式传递给printf函数。

It misdone via stack

push eax将要打印的字符串地址放到栈上
call printf读取栈顶(the top of the stack)的地址并打印出字符串
printf执行完后不会自动移除栈顶的地址
因此需要使用add esp, 4来清理(clean)栈,移除这4个字节的参数。
否则,栈中的数据会保持不变,影响后续操作

Q&A

  1. 列出Pentium处理器中Flag寄存器的3个使用场景:
    • Flag寄存器可以在以下场景中使用:
    1. 条件跳转操作(例如,使用CMP指令后,通过零标志判断两个值是否相等)。
    2. 算术操作中检测溢出(通过溢出标志位检测溢出)。
    3. 中断处理(设置或清除中断标志来控制中断)。
  2. Flag寄存器可以用于在一条指令和后续指令之间传递信息(对或错?):
    • 答案:对。
  3. 哪一个寄存器用于存储此指令的减法结果?CMP AL, BL:
    • 答案:AL寄存器(AL为目标寄存器,存储比较结果的标志位会反映在Flag寄存器中)。
  4. 在内联汇编中,如何传递参数给printf:
    • 这里的问题是关于在内联汇编中,如何将参数传递给printf函数,正确的方式是通过堆栈(stack)来传递参数,利用push eax等操作将值压入堆栈。printf读取栈上的参数地址进行输出。
    5. 内联汇编可以调用C库函数(True or False):
    • True。
  5. 状态标志在指令执行之前是否被设置:
    • 实际上,状态标志是在指令执行之后根据结果进行更新的,False。
  6. D标志用于设置循环方向:
    • D标志通常用于控制字符串操作指令(如MOVSB、STOSB)中的递增或递减方向。

http://www.kler.cn/news/357993.html

相关文章:

  • Python 多线程学习与使用
  • 《计算机视觉》—— 基于 dlib 库的方法将两张人脸图片进行换脸
  • React Agent 自定义实现
  • 记录 Latex 中 align 环境下, 两个对齐
  • 在Ubuntu上安装Docker以及使用
  • Linux服务器前后端项目部署vue+springboot—搭建服务器上的运行环境(JDK、Redis、MySQL、Nginx)
  • 十四、行为型(观察者模式)
  • Netty无锁化设计之对象池实现
  • C语言(函数)—函数栈帧的创建和销毁
  • 机器学习与神经网络:诺贝尔物理学奖的新纪元
  • tensorRT_Pro自学记录
  • Java_EE 网络编程(TCP与UDP通信)
  • 类与对象(三)
  • 2024-10-16 学习人工智能的Day8
  • 【设计模式】深入理解Python中的适配器模式(Adapter Pattern)
  • Spring Boot中使用FlexyPool动态监控管理数据库连接池
  • 自己用react开发了一张Es6的学习页面(持续更新系列)
  • 【计算机网络 - 基础问题】每日 3 题(四十七)
  • AI核身-金融场景凭证篡改检测YOLO原理
  • Redux (八) 路由React-router、嵌套路由、路由传参、路由懒加载