CPU的控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。由运算器执行对应的机器指令,并将结果通过地址总线写回数据段
中间处理器(CPU,Central Processing Unit)是一块超大规模的集成电路,是一台计算机中的控制核心和运算核心。它的主要功能是翻译程序指令和进行数据处理。
中间处理器主要由运算器(算数逻辑运算单元,ALU,Arithmetic Logic Unit)和缓冲存储器(Cache)组成,也包括能实现它们之间联系的数据、控制单元和总线。中间处理器的主要作用是将程序中的指令翻译成机器指令供机器识别,继而实现数据的处理。所以,CPU的根本任务就是执行程序指令。
CPU的根本任务就是执行指令,对计算机来说最终都是一串由“0”和“1”组成的序列。CPU从逻辑上可以划分成3个模块,分别是控制单元、运算单元和存储单元,这三部分由CPU内部总线连接起来。如下所示:
指令计数器(IC、Instruction Counter)
指令寄存器(IR、Instruction Register)
指令译码器(ID、Instruction Decoder)
操作控制器(OC、Opreater)
1、指令计数器 IC
指令计数器又称程序计数器,是中间处理器内的一个寄存器,其作用是存放当前正在进行的指令的地址。当指令计算器中指令被取出后,计数器内的地址加一或者指针下移一位,此时计数器内的地址即为下一条指令的地址。
2、指令寄存器 IR
指令寄存器的主要功能是存储将要执行的程序指令。由指令计数器中的指令地址找到内存中相对应的指令地址后,处理器将此条指令存储进指令寄存器中,以供后续使用。
3、指令译码器 ID
程序指令存储进处理器的指令寄存器后,由指令译码器将由高级语言表示的指令翻译成计算机能够识别的机器语言。
4、操作控制器
操作控制器的功能就是根据指令操作码和时序信号,产生各种操作控制信号,发送给运算单元,完成取指令和执行操作的控制。
计算机的运算包括算术运算(如加减乘除)和逻辑运算。 处理器内的运算单元接收控制单元发出的信号和存储单元内的对应数据,实现操作的执行。
故,运算单元是中间控制器内的执行单元,它的所有动作都按照控制单元发出的信号进行。
CPU的存储单元主要由CPU的内缓存寄存器和寄存器组构成,主要用于暂时存放数据(包括待处理的和已处理的数据)。
由于CPU访问自身内寄存器的访问速度比CPU访问系统内存数据的访问速度快得多,所以,将内存中的数据读取进CPU的内缓存器中,减少了CPU访问系统内存的次数,提 高了访问效率和CPU的工作速度。
CPU的控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。由运算器执行对应的机器指令,并将结果通过地址总线写回数据段
1、(读)取指令:CPU的控制器从内存读取一条指令并放入指令寄存器。指令的格式一般是这个样子滴:
操作码就是汇编语言里的mov,add,jmp等符号码;操作数地址说明该指令需要的操作数所在的地方,是在内存里还是在CPU的内部寄存器里。
2、译码指令(解码):指令寄存器中的指令经过译码,决定该指令应进行何种操作(就是指令里的操作码)、操作数在哪里(操作数的地址)。
3、 执行指令,分两个阶段“取操作数”和“进行运算”。
4、 修改指令计数器,决定下一条指令的地址。
下面文章来源:
https://blog.csdn.net/MiracleWW/article/details/114711039
(https://blog.csdn.net/MiracleWW/article/details/114711039)
机器指令的二进制表示方式,是指令在程序存储器中存放形式,是CPU可直接读取、识别并执行的唯一形式。程序代码的本质就是一条一条的指令,我们需要通过编码的方式让CPU知道我们需要它干什么,最后由译码器翻译成一条条的机器指令。机器指令主要有两部分组成:
操作码:表征指令的操作特性与功能 (指令的唯一标识) 不同的指令操作码不能相同,每一种编码代表一种指令;操作码是指令的一部分,它告诉处理器应该要做什么。它包含表示 CPU 要执行的实际操作的说明。操作码类似汇编语言里的mov,add,jmp等符号码,在单片机内都是机器语言,二进制数。组成操作码字段的位数一般取决于计算机指令系统的规模。例如,一个指令系统只有8条指令,则有3位操作码就够;如果有32条指令,那么就需要5位操作码。
二、操作码分类
操作码可以分为固定长度的代码和可变长度的代码
1、固定长度的代码
所有指令操作码的长度相等
例如:某计算机共有64条指令,采用固定长度操作码,需要6位编码,从000000~111111 2的6次方为64
优点:编码方法简单,便于指令的译码
缺点:不方便指令系统中增加新的指令(扩展不方便)
2、可变长度的代码
不等长的指令操作码可以减小操作码的平均长度,提高指令编码的效率,从指令的扩展性来看,也希望操作码长度可变。
这是操作码不固定的指令格式,四位是基本的操作码,还可以扩充,但是指令的字数不变,就是说把不用的地址码部分可以做操作码用。
例如:假设某机器的指令长度为16位,包括4位基本操作码和三个4位地址码段。
①表示三地址指令:因有4位操作码则能表示16条;②表示二地址指令:因有8位操作码则可表示256条;③表示一地址指令:因有12位操作码则可表示4096条
如果需要三地址、二地址、一地址指令各15条、零地址指令16条,则一样能够采用可变格式操作码实现。例如可以这样规定:
15条三地址指令的操作码为:0000 ~ 1110 (操作码4位可表示16条指令,由于只有15条,所以还剩余一种状态1111,可以做二地址指令的标记)
15条二地址指令的操作码为:前4位1111, 即 1111 0000 ~ 1111 1110
15条一地址指令的操作码为:前8位均为1, 即 11111111 0000 ~11111111 1110
16条零地址指令的操作码为:前12位均为1, 即 1111111111110000~1111111111111111
我当时看的时候也想问为什么 不过最后我知道了不是一定要 多少条的,是为了方便 才 15条的。(操作码4位可表示16条指令,由于只有15条,所以还剩余一种状态1111,可以做二地址指令的标记)
例如:设某台计算机有100条指令,要求:
采用固定长度操作码编码,请设计其操作码编码
假设这100条指令中有10条指令的使用改了达到90%。其余90条指令的使用概率达到10%.
请采用不定长编码设计一种操作码编码的方案,并求出操作码的平均长度(常用指令用短编码 不常用指令用长编码)
答、1)采用固定长度操作码编码,需要7位操作码,取其中100个代码作为操作码 0000000~1100011之间的代码代表100条指令(长度为7)
其余的1100100~1111111共28个代码可用于增加新的操作码
2)采用不固定长度的操作码编码,可以用4位代码对10条指令进行编码,用8位代码对90条指令进行编码。00001001对应10条常用的指令,从未使用的10101111的代码后面扩展4位
10100000~10101111 16个
10110000~10111111 16个
11000000~11001111 16个
11010000~11011111 16个
11100000~11101111 16个
11110000~11111001 10个
已上的1010 ,0000~1111,1001之间的90个代码表示其余90条指令
指令操作码的平均长度为:
4*90%+8*10%=4.4 小于等长编码的7位
地址码
地址码直接给出操作数和操作数的地址(操作数地址说明该指令需要的操作数所在的地方,是在内存里还是在CPU的内部寄存器里,地址码中可以包含存储器地址。也可包含寄存器编号。)
指令中可以有一个、两个或者三个操作数,也可没有操作数,根据一条指令有几个操作数地址,可将指令分为零地址指令。一地址指令、二地址指令、三地址指令。4个地址码的指令很少被使用。
操作码字段 地址码
操作码 A1 A2 A3 三指令地址
操作码 A1 A2 二指令地址
操作码 A1 一指令地址
操作码 零指令地址
eg: INC AL
INC [S1]
二地址指令:指令编码中有两个地址,分别指出了参加操作的两个操作数的存储位置,结果存储在其中一个地址中
(op a1,a2:a1 op a2 a1)
eg: MOV AL,BL
ADD AL,30
三地址指令:指令编码中有3个地址码,指出了参加操作的两个操作数的存储位置和一个结果的地址
(op a1,a2,a3: a1 op a2 a3)
二地址指令格式中,从操作数的物理位置来说有可归为三种类型
寄存器-寄存器(RR)型指令:需要多个通用寄存器或个别专用寄存器,从寄存器中取操作数,把操作结果放入另一个寄存器,机器执行寄存器-寄存器型的指令非常快,不需要访存。
寄存器-存储器(RS)型指令:执行此类指令时,既要访问内存单元,又要访问寄存器。
存储器-存储器(SS)型指令:操作时都是涉及内存单元,参与操作的数都是放在内存里,从内存某单元中取操作数,操作结果存放至内存另一单元中,因此机器执行指令需要多次访问内存。
上例程序的执行过程描述如下:
PC中的初始地址值为2000H
(1) CPU取出2000H单元中指令首字节(操作码)E5H, PC+1=2001H,译:本指令尚有第二字节直接地址
(2) CPU取出2001H单元中操作数3AH,PC+1=2002H,执行:将3AH中数送入A。
(3) CPU取出2002H单元中指令操作码85H,PC+1=2003H,译:尚有二字节地址码
(4) CPU取出2003H单元中地址码3BH,PC+1=2004H,取出3BH单元中的数
(5) CPU取出2004H单元中地址码3AH,PC+1=2005H,执行:将数送入3AH单元
(6) CPU取出2005H单元中指令操作码F5H,PC+1=2006H,译:尚有一字节地址码
(7) CPU取出2006H单元中地址码3BH,PC+1=2007H,执行:将A中数据送入3BH单元
(8) CPU取出2007H单元指令操作码02H,PC+1=2008H,译:尚有二字节目标地址
(9) CPU取出2008H单元中地址高字节20H,PC+1=2009H,尚有一字节地址码
(10) CPU取出2009H单元中地址低字节07H,PC+1=200AH,执行:2007H PC
转步骤(8)无限循环
补充:
PC(程序计数器,程序指针)的作用:
PC:始终存放着CPU要取出执行的下一个机器指令代码的存放单元的地址。
或:始终指向要取出执行的下一条指令代码存放单元,
∵51单片机程序存储器空间为64KB,使用16bit地址码
∴51单片机的PC为16bit(双字节)
PC的作用:
● 51单片机复位后,初始化PC=0000H,∴51单片机总是以0000H地址开始执行程序的。51单片机程序存储器空间中,0000H地址处一定要有存储器并存放有效的程序指令。
● CPU总是以PC中的内容为地址从程序存储器中取指令代码的。
● CPU每取出一个字节的指令代码自动地将PC加1(PC←PC+1)实现程序的顺序执行逻辑。
● CPU执行跳转指令功能时为将指令指定的跳转目标地址置入PC(使下一次取的是该地址处的指令)实现了程序跳转执行逻辑。
图1为程序计数器的示意图。图中,假定(1)执行地址1000h中的指令,(2)执行地址1000h中的指令后,程序计数器的値自动增加一个量并显示出下一个地址1001h,接下来,(3)CPU执行地址1001h中的指令。
控制器(CU)和运算器(ALU)
PC寄存器(程序计数器):用于存放下一条需要执行的指令地址信息,注意是下一条指令的地址!
指令寄存器:用于存放当前正在执行的指令,注意是当前的指令!
指令译码器:用于翻译指令信息,与指令寄存器相连
那么一条指令的执行就应该包含
1.取指令:CU根据PC寄存器中的指令地址,去内存或者指令缓存中获取具体的指令,并加载到指令寄存器中。即完成了“取指令”操作。与此同时,指令计数器的地址变成下一条指令的地址。
2.分析指令:从内存中取出的指令不能被计算机直接识别。通过指令译码器的处理,寄存在指令寄存器中的指令变成能够被计算机直接识别的机器语言以实现指令的执行。拿到指令之后,指令译码器将指令寄存器中的指令进行翻译,向ALU发起控制信号和指令信息。此时PC寄存器自增即+1,加载下一个要执行的指令地址(至少此时是这样的,此为顺序寻址)。
3.执行指令操作控制器将译码后的指令发送给相应的运算单元,运算单元结合存储单元内的数据,对数据进行进行相应的操作。ALU拿到相关信息之后进行相关的计算。
4.访问内存(或缓存):如果涉及到相关数据需要去缓存中拿,那就去取数据。
5.数据写回:将计算结果写入到寄存器中,写入到缓存中,甚至写入到内存中。
此部分引用文章,如有侵权请联系作者删除
原文链接:https://blog.csdn.net/yechongbinbin/article/details/115146615
代码在单片机上跑起来,一般有6个步骤:
1.写好源文件。
2.准备好启动文件(一般半导体厂商提供,也可以自己根据需求进行裁剪编写)。这个文件直接是用汇编写的,主要完成三个工作:初始化堆栈,定义中断向量表,进行复位中断(初始化寄存器、时钟和跳转到main函数)。
(1)初始化堆栈:主要定义栈、堆地址、大小和一些格式
(2)定义中断向量表:可以简单理解为各个小函数的地址。要放在Flash的0位置处。例如要运行Reset_Handler这个中断处理函数,PC就会先先访问这个中断向量表,得知Reset_Handler在哪个地址,然后PC跳到相应的位置开始执行Reset_Handler, 里面定义了各种中断发生后CPU要做的事。
(3)进行复位中断:单片机一上电复位或者按下复位按钮执行的初始化操作。首先进行系统初始化,然后会跳转到我们写的应用程序main函数。
3.编译生成二进制机器码。把写的代码变成机器可以认识的二进制文件。一般用集成好的开发环境如MDK,包括了预处理、编译、链接、加上地址头等等,最终生成特定MCU的二进制文件。
4.烧写单片机的FLASH。将第三步生成的二进制文件通过烧录器、仿真器等媒介烧录进FALSH。当然不一定是FLASH,还可以是其他存储芯片。
5.复位。烧录完复位一下。让芯片里的各个寄存器恢复到初始状态,主要让CPU的PC寄存器恢复到0,重头开始跑复位中断处理。
6.运行用户代码。复位后跳转到main函数执行。
看到这里你肯定会想,并不是所有的指令都是顺序执行的,比如if····else,比如方法调用,此时PC寄存器也是自增加1嘛?我们来讲讲if····else和方法调用。
先来看if····else,下边有一小块代码
int r = 1;
int a = 10;
if (r == 0)
{
a = 1;
} else {
a = 2;
}
很简单的一个if判断以C语言为例 if部分翻译为汇编代码就是
if (r == 0)
3b: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0
3f: 75 09 jne 4a <main+0x4a>
{
a = 1;
41: c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1
48: eb 07 jmp 51 <main+0x51>
}
else
{
a = 2;
4a: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2
51: b8 00 00 00 00 mov eax,0x0
}
可以看到,这里对于 r == 0 的条件判断,被编译成了 cmp 和 jne 这两条指令。
这两条指令中cmp表示比较的意思,这明显是一条二地址指令,cmp是操作码,后边两个是地址码,意思就是将rbp寄存器中偏移量为4的位置的数取32位(DWORD PTR)出来,和0(0x0)比较,并将比较结果存到条件码寄存器中。
跟着的 jne 指令,是 jump if not equal 的意思,它会查看对应的零标志位。如果为 0,会跳转到后面跟着的操作数 4a 的位置。这个 4a,对应这里汇编代码的行号,也就是上面设置的 else 条件里的第一条指令。当跳转发生的时候,PC 寄存器就不再是自增变成下一条指令的地址,而是被直接设置成这里的 4a 这个地址。
那么CPU 再把 4a 地址里的指令加载到指令寄存器中来执行。mov 指令把 2 设置到对应的寄存器里去,相当于一个赋值操作。然后,PC 寄存器里的值继续自增,执行下一条 mov 指令。
最后一条mov指令的第一个操作数 eax,代表累加寄存器,第二个操作数 0x0 则是 16 进制的 0 的表示。这条指令其实没有实际的作用,代表我执行完了,给 main 函数生成了一个默认的为 0 的返回值到累加器里面。
然后是if条件之后我们注意到有个jmp指令,它之前的mov指令和4a是一样的意思,jmp指令,也就是jump的缩写,这是一个无条件跳转指令。跳转的地址就是这一行的地址 51,也就是说这个指令同样会改变PC寄存器中的地址信息,即将PC寄存器中的值设置为51(51大家都能使用,没必要生成两条指令)。具体流程如图所示,for循环也是同样的道理,不过涉及到具体的指令意义需要大家自己探索,但是流程是一样的,都是通过指令改变PC寄存器中的指令地址,引导CPU执行自己想要执行的指令。