指令
伪指令不属于单片机的指令系统,而是由汇编器提供的指令,用于调整存储器中程序的位置、定义宏、初始化存储器等。AVR单片机的汇编器共提供18条伪指令(见附表)。
其中,ORG、DB、DW、EQU读者比较熟悉,这里不再赘述。下面对部分伪指令加以说明。
BYTE-保存单字节数据到SRAM中。BYTE伪指令仅用在数据存储器。为提供数据保存的位置,在BYTE前应有标号。在由CSEG、ESEG定义的代码段和E2PROM段中不能使用BYTE伪指令。
格式LABEL:.BYTE表达式
CSEG-定义程序存储器代码段的起始位置一个汇编文件可以包括若干个代码段,汇编时这些代码段被连成一个代码段。在代码段中不能使用BYTE伪指令。
格式.CSEG
DSEG-定义数据段的起始位置一个汇编文件,可以包括若干个数据段,汇编时这些数据段被连成一个数据段。数据段等由BYTE伪指令组成。
格式.DSEG
ESEG-定义E2PROM段的起始位置一个汇编文件可以包括若干个E2PROM段,汇编时这些EEPROM段被连成一个EEPROM段。在EEPROM段中不能使用BYTE伪指令。
格式.ESE
GDEF-为寄存器设置符号名DEF伪指令允许周符号代替寄存器。一个寄存器可以赋多个符号。
格式.DEF符号=寄存器
DEVICE-定义使用的器件DEVICE定义程序所使用的器件,若程序中有指定器件不支持的指令,或程序段、E2PROM段超出指定器件的容量,汇编器将给出提示。若不用DEVICE伪指令,则默认器件支持所有指令,也不限制存储器容量。
格式.DEVICEAT90S1200|AT90S2313|AT90S4414|AT90S8515
AVR单片机的器件不同,指令的条数不同。
AT90S1200有89条最基本指令;Attinyll/12/15/22有90条指令:AT90S2313/2323/2343/2333/4414/4433/4434/8515/90S8534/8535有118条指令;ATmega603/103有121条指令;ATmega161有130条指令。因此在源程序中必须先用伪指令“DEVICE”定义使用的器件名。
INCLUDE-包括另外的文件INCLUDE告诉汇编器从指定的文件开。始读,然后汇编该文件,直至文件结束或遇到EXIT伪指令。
格式 INCLUDE“文件名”
EXIT-汇编结束若EXIT出现在包括文件中,则从文件中INCLUDE伪指令行继续执行。
来源: 广电电器
单片机执行程序的过程,实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行。即取指令-----分析指令-----执行指令。
取指令的任务是:根据程序计数器PC中的值从程序存储器读出现行指令,送到指令寄存器。
分析指令阶段的任务是:将指令寄存器中的指令操作码取出后进行译码,分析其指令性质。如指令要求操作数,则寻找操作数地址。
计算机执行程序的过程实际上就是逐条指令地重复上述操作过程,直至遇到停机指令可循环等待指令。
一般计算机进行工作时,首先要通过外部设备把程序和数据通过输入接口电路和数据总线送入到存储器,然后逐条取出执行。但单片机中的程序一般事先我们都已通过写入器固化在片内或片外程序存储器中。因而一开机即可执行指令。
下面我们将举个实例来说明指令的执行过程:
开机时,程序计算器PC变为0000H。然后单片机在时序电路作用下自动进入执行程序过程。执行过程实际上就是取出指令(取出存储器中事先存放的指令阶段)和执行指令(分析和执行指令)的循环过程。
例如执行指令:MOV A,#0E0H,其机器码为“74H E0H”,该指令的功能是把操作数E0H送入累加器,0000H单元中已存放74H,0001H单元中已存放E0H。当单片机开始运行时,首先是进入取指阶段,其次序是:
1 程序计数器的内容(这时是0000H)送到地址寄存器;
2 程序计数器的内容自动加1(变为0001H);
3 地址寄存器的内容(0000H)通过内部地址总线送到存储器,以存储器中地址译码电跟,使地址为0000H的单元被选中;
4 CPU使读控制线有效;
5 在读命令控制下被选中存储器单元的内容(此时应为74H)送到内部数据总线上,因为是取指阶段,所以该内容通过数据总线被送到指令寄存器。
至此,取指阶段完成,进入译码分析和执行指令阶段。
由于本次进入指令寄存器中的内容是74H(操作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H操作码的微操作系列,使数字E0H从0001H单元取出。因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC=0002H,PC在CPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。
转自: 21ic电子网
单片机执行指令的过程
judy 在 提交
MCS- 51系列单片机的指令系统是一种简明高效的指令系统,其基本指令共有111条,其中单字节指令49条,双字节指令4'5条,三字节指令17条。如果按功能可以讲这些指令分为五类:数据传送类(29条)、算术操作类(24条)、逻辑操作类(24条)、控制转移类(17条)以及位变量操作类(17条)。对于反向设计而言,我们关心的不是它的各种具体指令的多少而是指令的寻址方式。所谓的寻址方式就是寻找确定参与操作的数的真正地址。MCS-51系列单片机的111条指令一共只采用了5种寻址方式。5种寻址方式以及它们的寻址空间如表1所示。
1.寄存器寻址
寄存器寻址方式可用于访问选定寄存器区的8个工作寄存器RO-R7。由指令操作码的低三位指示所用的寄存器,寄存器A, B, DPTR, AB和Cy位(位处理机的累加器)也可作为寻址对象。在这种寻址方式中被寻址的寄存器的内容就是操作数。
在实现这类寻址方式时,确定被寻址寄存器的物理地址时关键。由于选定寄存器区由PSW的相关位来决定,指令的低三位又指示了具体的寄存器,所以可以用下面的VHDL语句来确定相关寄存器的物理地址:
rr_ adr<=unsigned((psw and“00011000”)or(rom_data_i and“00000111”));
其中rr_ adr表示的是寄存器的物理地址,rom_data_i表示的是指令代码。物理地址确定以后,对RAM的操作就很好进行。
2.直接寻址
直接寻址是访问特殊功能寄存器的唯一方法。它也可以用于访问内部RAM(128个字节)。采用直接寻址方式的指令是双字节指令,其中第一个字节是操作码,第二个字节是内部RAM或特殊功能寄存器的直接地址。地址已经给出,显然不需要像寄存器寻址那样先计算地址,直接针对由ROM给出地址就可以对RAM进行相关操作。
3.寄存器间接寻址
寄存器间接寻址可用于访问内部RAM或者外部数据存储器。访问访问内部RAM或者外部数据存储器的低256个字节时,可以采用RO或R1作为间址寄存器。这类指令为单字节指令,其最低为表示采用RO还是R1作为间址寄存器。访问内部RAM和外部数据存储器时采用不同的指令,所以不会引起混淆。
访问外部数据存储器,还可用数据指针DPTR作为间址寄存器,DPTR是16位寄存器,故它可对整个外部数据存储器空Il(64K)寻址。
在执行PUSH(压栈)POP(出栈)指令时,也采用寄存器间接寻址,这时堆栈指针SP用作间址寄存器。
实现这类寻址方式必须分为两步,首先确定4个通用工作寄存器区中可以作为间接寻址寄存器的8个单元的地址,然后读出所选定的寄存器中的值,这个值就是当前指令要寻址的空间的物理地址。确定间接寻址寄存器的地址可以由下面的VHDL语句来实现:
ri_adr<=((psw and“00011000”)or(s-command (7downto 0) and“110000000111”)),
其中ri_ adr表示的是用于间接寻址的寄存器的物理地址,s_command表示的是当前指令的操作码。然后通过另外一个读RAM的进程就可以确定间址寄存器中的值,从而得到指令需要的存储器单元的地址。至千其他特殊功能寄存器作为间址寄存器的情况,由于间址寄存器的地址事先已经能够确定,所以这种方式下寻找存储器单元的地址就仅需要上述两步中的后一个步骤就可以确定指令需要的存储器单元的地址。
4.立即寻址
采用立即寻址方式的指令是双字节的,第一个字节是操作码,第二个字节是立即操作数。因此,这种寻址方式实现起来比直接寻址还要容易,操作数就是放在程序存储器内的常数。
5.基址寄存器加变址寄存器间接寻址
这种寻址方式用于访问程序存储器的一个单元,该单元的地址是基址寄存器(DPTR或PC)与变址寄存器A的内容之和。虽然这类寻址方式也是间接寻址,对于使用DPTR作为基址寄存器的情况,它的实现方法和用数据指针DPTR作为间址寄存器时的间接寻址的情况很相似,因为其间址寄存器事实上还是确定的。与用数据指针DPTR作为间址寄存器时的间接寻址的情况有所不同的是,确认最终需要的地址还要进行一次加法运算,这在使用VHDL语言描述的时候是很容易实现的。用PC作为基址寄存器时,需要知道PC当前值,但是PC和DPTR是不同的,DPTR是特殊功能寄存器,利用它的地址就可以读出其值,PC并没有被分配地址,不能使用读RAM的方式取得其值,所以内部有必要设置编写这样一个进程,它用于读出那些位于CPU内部、没有分配地址的辅助寄存器的值。
来源: EEPW
最近在基于51单片机编程的过程中出现了个很奇怪的问题 “程序执行中在寄存器EA=1,ET0=1,TR0=1条件下,单TF0=1时并没有执行中断”。在有过单片机中断编程经历者都知道当EA=1,ET0=1的条件下,满足TF0=1时,如果在此期间没有更高优先级的中断执行的情况下定时器中断0必定会产生中断响应。而在我所编写的程序中仅使用了定时器中断0,一个中断也就谈不上存在优先级问题。
经过我对自己程序的检查并对各教材中断程序对比发现我的程序中的一个问题:由于中断的不可控性决定其跳出中断返回主程序的不确定,而由于程序需要中断跳出后能跳到指定的地址。为了解决这个问题我在中断结束的地方直接用了无条件跳转指令“LJMP ADR16”其中ADR16是我想在中断结束后程序所运行的地址,而没有经过指令“RE”。
问题找到了这就意味着我的程序和其他程序不同的地方就是没有执行“RE”而直接跳出。
为了解决问题所在我查阅了很多单片机方面的资料,教材。几乎所有的教材对指令“RETI”的作用千篇一律都是:“中断程序完成后,一定要执行一条RETI指令,执行这条指令后,CPU将会把堆栈中保存着的地址取出,送回PC,那么程序就会从主程序的中断处继续往下执行了。”如果“RETI”的作用仅仅在于“把堆栈中保存着的地址取出送回PC”;那么我用指令“POP DPH”和“POP DPL”两条指令取代其做用不就可以达到同样的推出地址的效果么?这样可以解决由于只有进堆栈指令(硬件自动生成)没有出堆栈所导致的堆栈溢出错误,但是并不能解决文章开始所提到的“进不了中断”问题。这让我更加相信书上所介绍的关于指令“RETI”作用并不完全。
经过查阅各种资料文献,我发现了个以往在介绍单片机硬件,以及寄存器上教材,老师,没有提及的“‘优先级生效’触发器“的概念。资料指出“根据8051的结构特点,其中断系统中含有两个不可寻址的“优先级生效”触发器。一个用于指出CPU是否正在执行高优先级的中断服务程序,这个触发器为1时,系统将屏蔽所有的中断请求;另一个则指出CPU是否正在执行低优先级中断服务程序,该触发器为1时,将阻止除高优先级以外的一切中断请求。由此可见,若要响应同级甚至是低级中断请求,必须使得该“优先级生效”触发器清零。但该触发器又是不可寻址的,所以无法用软件直接清零。”问题是不是在这里呢?而“优先级生效”触发器清零过程是怎样执行的呢?是在硬件自动执行的那么是在什么时候执行的呢?带着问题我去解决问题。假设我可以将程序满足跳出中断后跳到自己原来指定地址“ADR16”又满足执行指令“RETI”。经过反复思考我用“DEC SP”;“DEC SP”;“MOV DPTR,#ADR16”;“PUSH DPL”;“PUSH DPL”“PUSH DPH”四条指令代替,问题得到了解决。
总结:
中断指令“RETI”做为中断跳出指令除了将堆栈中保存着的地址取出,送回PC;使程序从主程序的中断处继续往下执行。的作用外还有将“优先级生效”触发器清零。自己做的程序也是出现了这个错误,由于对“优先级生效”触发器清零,导致第二次进不了中断(相当于同优先级申请)。
后记:
在解决这个问题时候我所用的知识是课本上的,而又不完全是课本上的。在这个过程中我用已学的知识解决了自己的问题,并进一步推出中断过程的一些新的知识,我认为新知识的学习有很大程度的要靠自己在已学过知识的基础上通过运用,总结,推导等过程获得新知识。着也是当代大学生运用知识,获取新知识的一种能力。
转自: 电子发烧友