精华内容
下载资源
问答
  • 前言笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地...

    前言

    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地阐述这个概念。

    中断的概念

    当 CPU 正在处理某件事情的时候,外部发生的某一事件请求 CPU 迅速去处理,于是,CPU 暂时中止当前的工作,转去处理所发生的事件。中断服务处理完该事件以后,再回到原来被中止的地方,继续原来的工作,这样的过程称之为中断,示意图如下:

    195249593_1_20200710015203364

    中断响应及处理过程

    回顾函数调用的过程,子程序由主程序进行调用,从而完成执行。但是中断服务子程序并没有被主程序进行调用,中断服务子程序的执行是通过中断请求完成的,也就是说中断服务子程序可以发生在主程序执行的随意位置,那现在就面临一个问题了,如果当CPU 正在执行函数调用的子程序的内容的时候产生了一个中断请求,那么这个时候 CPU 将暂停执行函数调用的子程序的内容,转而去执行中断服务子程序的内容,如果不进行额外的处理,那么函数调用的子程序的相关数据将丢失,因此在执行中断服务子程序之前,CPU 必须要保存发生中断的那个地方的相关信息,这个操作用专业的术语来讲就是保护现场,保护现场之后,CPU 将执行中断服务子程序的内容,执行完中断服务子程序的内容之后,CPU 要回到刚刚暂停的地方继续执行,另外在返回之前,CPU 还要进行恢复现场,恢复现场之后,就可以返回到暂停的地方继续执行了,下面是整个过程的示意图:

    195249593_2_20200710015203505

    通过上述示意图我们也可以看到在返回地址这个地方,中断服务子程序和函数调用子程序的返回地址所遵循的原理是一样的,函数调用子程序的返回地址是函数调用指令的下一条指令的地址,而在上述示意图中的 N 和 N 1 的含义也是类似的,当 CPU 执行到第 N 条指令的时候,CPU 接收到了一个中断请求,在执行完第 N 条指令之后,转而去执行中断服务子程序的内容,然后中断服务子程序的返回地址对应的是第 N 1 条指令的地址。

    中断的堆栈占用

    在刚刚所述的内容中,说到 CPU 在执行中断服务子程序的内容之前,需要保护现场,那保护现场这个操作具体是怎么实现的呢?这个时候,就要用到我们的堆栈了。在这里拿 ARM Cortex M3 举例,在响应中断时所做的第一个操作就是保护现场,它会依次把 xPSR,PC,LR,R12以及 R3-R0 由硬件自动压入适当的堆栈中,注意,这里是自动压入堆栈,也就是说如果我们看对应的汇编代码是看不到这部分压栈操作的。另外,我们知道对于 ARM Cortex M3 的堆栈来说,它存在两个,一个是主堆栈指针(MSP),一个是线程堆栈指针(PSP),其中主堆栈指针是复位后默认使用的堆栈指针,用于操作系统内核和中断处理程序,线程堆栈指针(PSP)是由用户的应用程序代码所使用。那么在执行现场保护时将相关寄存器的值压入堆栈,应该使用哪个堆栈指针呢?这也是存在一个原则的,如果在响应中断时,当前的代码正在使用线程堆栈指针(PSP),那么将使用线程堆栈指针(PSP)进行压栈,否则将使用主堆栈指针(MSP)。另外在 CPU 进入中断服务子程序之后,所涉及的堆栈操作所使用的堆栈一直是主堆栈指针(MSP)。为了更直观的展示这个过程,下图是发生中断请求后,堆栈的变化示意图:

    195249593_3_20200710015203552

    通过上图我们可以很清楚地看到在响应中断时产生的保护现场操作,堆栈明显增长了,而在执行完中断服务子程序的内容之后,又将执行恢复现场的操作,这个时候堆栈的内容又减少了。

    为了更清楚地展示压入堆栈寄存器的操作,笔者在这里也给出上述图中堆栈粉色部分的详细内容,图片如下:

    195249593_4_20200710015203646

    上述就是保护现场时所压入堆栈的相关寄存器,另外还需注意的一点是当所涉及的中断服务子程序逻辑比较复杂的时候,就需要更多的寄存器了,这个时候就需要用到 R4-R11 了,但是这部分寄存器是不能进行自动压栈的,也就是说如果在中断服务子程序中使用到这部分寄存器的时候就需要进行手动压栈,那么这部分的压栈操作在汇编层面就能看到了。

    中断向量表

    在上述所阐述的内容中,我们知道了中断会在主程序的任意发生中断请求,从而执行中断服务子程序的内容,也阐述了在执行中断服务子程序的内容之前需要进行保护现场的操作,以及执行完中断服务子程序的内容之后需要进行恢复现场。现在我们再来思考,在 CPU 中,中断源不止一种,可以是按键按下所触发的一个外部中断,也可能是在使用串行通信时,收到数据所触发的一个中断,亦或者在 CPU 中定义的一个定时中断由于设置的时间到了而触发的定时中断,这个时候,就浮现一个问题了,要如何将这一个一个的中断源与其各自的中断服务子程序所一一对应起来呢?换句更为通俗的话来讲就是当 CPU 接收到一个中断信号时,CPU 将如何找到对应的中断服务子程序进行执行呢?这个时候,就需要中断向量表了,下面是中断向量表的特点:

    中断向量表在 CPU 中是一段连续的存储空间

    中断向量表在 CPU 复位后有默认的起始地址

    每一个中断在中断向量表中都有对应的表项,该表项的值为该中断源对应的中断服务程序的地址

    由程序代码确定中断向量表中的每个表项

    上述特点说中断向量表都存在默认的起始地址,在这里依旧拿 ARM Cortex M3 内核来看,它的中断向量表默认的起始地址是从地址 0x0000 0000 开始的,如下图所示:

    195249593_5_20200710015203724

    当然这只是一部分,并不是全部的表项。有了中断向量表之后,那么当 CPU 接收到中断请求的时候,就会根据这个中断请求的信号去查这个表,从而查找到其所对应的中断服务子程序的地址,然后将这个地址赋值给 PC 指针寄存器就,那么 CPU 就可以完成中断服务子程序的执行了,对于 PC 指针寄存器不是太清楚地朋友可以看笔者的这篇文章 《程序是如何在 CPU 中运行的(二)》。

    中断服务函数的写法

    中断服务函数的写法不同的 CPU 有各自不同的写法,对于 ARM Cortex M3 的 CPU 来说,因为其内核的特点,在执行完中断服务函数后的返回指令与普通函数调用的返回指令是一样的,因此中断服务函数的写法与 C 语言中普通函数的定义没有区别,比如下面是 STM32F103 的一个外部中断的服务函数

    void EXTI0_IRQHandler(void)

    {

    /* 确保是否产生了中断 */

    if(EXTI_GetITStatus(EXTI_Line0) != RESET)

    {

    /*用户代码*/

    /*清除中断标志位*/

    EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);

    }

    }1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    通过上述的代码我们可以看到中断服务函数的另一个特点,就是它的返回值和形参都为 void ,这也是由原因的,因为中断服务函数本来就不是由主程序进行调用的,既然中断服务函数不会被其他函数所调用,那么其返回值和形参自然是 void 了。

    上述说到是因为 ARM Cortex M3 的 CPU 在处理中断服务函数的返回地址时用的指令和普通函数调用时的返回地址的指令一致,所以才能够使中断服务函数的写法与普通 C 语言函数没有差异,下面举一个 51 单片机的定时器中断服务函数的例子:

    void InterruptTimer0() interrupt 1

    {

    /*省略*/

    }1

    2

    3

    4

    上述的这个中断服务函数, InterruptTimer0可以任意起,但是括号后面的是有严格规定的,为了 51 单片机能够进行中断处理,C51 编译器对函数进行了扩展,增加了一个扩展关键字interrupt,从而让 CPU 知道这个是一个中断服务函数。

    中断的嵌套

    C 语言函数能够进行嵌套调用,同样的中断服务函数也能够进行嵌套,同样的用一张图来表明中断的嵌套:

    195249593_6_20200710015203880

    可以看到中断的嵌套也是在消耗堆栈的,和使用函数嵌套调用一个道理,这里需要注意的是中断是存在优先级的,如果发生了一个比当前执行的中断优先级低的中断请求,那么新产生的中断请求会等待正在执行的中断执行完成之后才开始响应新的中断,如果产生的中断的优先级比当前的优先级要高,那么也就会像上图所示一样进行执行。另外需要注意的是,中断的优先级是有限的,也就是说中断嵌套的层数是有限的,如果再考虑堆栈溢出的话,那么中断嵌套的层数还和堆栈的大小有关。

    总结

    上述就是关于中断的相关内容,简单地叙述了中断是如何响应的,如何执行保护现场和恢复现场的操作,CPU 如何根据中断向量表找到对应的中断服务函数,以及中断的嵌套,这就是这次分享的全部内容啦~

    展开全文
  •  中断服务子程序设计的任务 中断服务子程序设计的基本任务有下列4条: (1)设置中断允许控制寄存器IE,允许相应的中断请求源中断。 (2)设置中断优先级寄存器IP,确定并分配所使用的中断源的优先级。 (3)若是外部...
  • 中断系统的运行必须与中断服务子程序配合才能正确使用。设计中断服务子程序需要首先明确以下几个问题。中断服务子程序设计的任务中断服务子程序设计的基本任务有下列4条:(1)设置中断允许控制寄存器IE,允许相应的...

    中断系统的运行必须与中断服务子程序配合才能正确使用。设计中断服务子程序需要首先明确以下几个问题。

    中断服务子程序设计的任务

    中断服务子程序设计的基本任务有下列4条:

    (1)设置中断允许控制寄存器IE,允许相应的中断请求源中断。

    (2)设置中断优先级寄存器IP,确定并分配所使用的中断源的优先级。

    (3)若是外部中断源,还要设置中断请求的触发方式IT1或IT0,以决定采用电平触发方式还是跳沿触发方式。

    (4)编写中断服务子程序,处理中断请求。

    前3条一般放在主程序的初始化程序段中。

    举例如下:例1、假设允许外部中断0中断,并设定它为高优先级中断,其他中断源为低优先级中断,采用跳沿触发方式。在主程序中可编写如下初始化程序段:

    7905144d0e018b50da744961858a0a3c.png

    采用中断时的主程序结构

    由于各中断人口地址是固定的,而程序又必须先从主程序起始地址OOOOH执行。所以,在OOOOH起始地址的几个字节中,要用无条件转移指令,跳转到主程序。另外,各中断人口地址之间依次相差8字节,中断服务子程序稍长就超过8字节,这样中断服务子程序就占用了其他的中断入口地址,影响其他中断源的中断处理。为此,一般在进入中断后,利用一条无条件转移指令,把中断服务子程序跳转到远离其他中断入口的人口地址处。

    常用的主程序结构如下:

    29b9b830001a94d602183f4b28434fe6.png

    注意:在以上的主程序结构中,如果有多个中断源,就对应有多个“ORG XlX2X3X4H”的中断入口地址,多个“中断入口地址”必须依次由小到大排列。主程序MAIN的起始地址Y1Y2Y3 Y4H根据具体情况来安排。

    中断服务子程序的流程

    AT89S51响应中断后,就进入中断服务子程序。中断服务子程序的基本流程如右图所示。

    e3351b8d51466379c555b31e4e35670e.png

    下面对有关中断服务子程序执行过程中的一些问题进行说明。

    (1)现场保护和现场恢复 所谓现场是指进人中断时,单片机中某些寄存器和存储器单元中的数据或状态。为了使中断服务子程序的执行不破坏这些数据或状态,以免在中断返回后影响主程序的运行,因此要把它们送入堆栈保存起来,这就是现场保护。

    现场保护一定要位于现场中断处理程序的前面。中断处理结束后,在返回主程序前,则需要把保存的现场内容从堆栈中弹出,以恢复那些寄存器和存储器单元中的原有内容,这就是现场恢复。

    现场恢复一定要位于中断处理的后面。AT89S51的堆栈操作指令“PUSH direct”和“POP direct”,主要是供现场保护和现场恢复使用的。至于要保护哪些内容,应该由用户根据中断处理程序的具体情况来决定。

    (2)关中断和开中断 右图中现场保护前和现场恢复前关中断是为了防止此时有高一级的中断进入,避免现场被破坏;在现场保护和现场恢复之后的开中断是为下一次的中断做好准备,也为了允许有更高级的中断进入。这样做的结果是,中断处理可以被打断,但原来的现场保护和现场恢复不允许更改,除了现场保护和现场恢复的片刻外,仍然保持着中断嵌套的功能。

    但有的时候,对于一个重要的中断,必须执行完毕,不允许被其他的中断嵌套。对此可在现场保护之前先关闭总中断开关位,彻底关闭其他中断请求,待中断处理完毕后再开总中断开关位中断。这样,就需要把图5-9中的“中断处理”步骤前后的“开中断”和“关中断”两个过程去掉。

    (3)中断处理 中断处理是中断源请求中断的具体目的。应用设计者应根据任务的具体要求来编写中断处理部分的程序。

    (4)中断返回 中断服务子程序的最后一条指令必须是返回指令RETI,它是中断服务程序结束的标志。CPU执行完这条指令后,把响应中断时所置l的不可寻址的优先级状态触发器清O,然后从堆栈中弹出栈顶上的两个字节的断点地址送到程序计数器PC,弹出的第一个字节送入PCH,弹出的第二个字节送入PCL,CPU从断点处重新执行被中断的主程序。

    例2、根据下图的中断服务子程序流程,编写中断服务程序。假设现场保护只需要将PSW寄存器和累加器A的内容压人堆栈中保护起来。

    一个典型的中断服务子程序如下:

    a56ca30cf5cd832943cb76637a8e179b.png

    上述程序有几点需要说明:

    (1)本例的现场保护假设仅仅涉及PSW和A的内容,如果还有其他需要保护的内容,只需要在相应的位置再加几条PUSH和POP指令即可。注意,对堆栈的操作是先进后出,次序不可颠倒。

    (2)中断服务子程序中的“中断处理程序段”,应用设计者应根据中断任务的具体要求来编写这部分中断处理程序。

    (3)如果该中断服务子程序不允许被其他中断所中断,可将“中断处理程序段”前后的“SETB EA”和“CLR EA”两条指令去掉。

    (4)中断服务子程序的最后一条指令必须是返回指令RETI,千万不可缺少,它是中断服务子程序结束的标志。CPU执行完这条指令后,返回断点处,重新执行被中断的主程序。

    展开全文
  • 什幺是子程序?*在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部份代码,由一个或多个语句块组成。它负责完成某项特定任务,...

    什幺是子程序?

    *

    在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部份代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

    *

    一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

    *

    函数在面向过程的语言中已经出现。是结构(Struct)和类(Class)的前身。本身就是对具有相关性语句的归类和对某过程的抽象。

    子程,子涵,还有子什幺好听?

    觉得带“子”,“梓”,等等同音的都不好听,据统计,目前孩子用这些字的已经占超高比例了,我这样生活圈子小的人,周围都有十几个叫子什幺的。

    子程子曰:"....." 中的两个"子"分别是什幺意思?

    子程子:前一个“子”字,意为夫子,引申为老师之意。后一个“子”字,为古代男子的尊称,意为先生。程子(公元1033~1107年),名颐,字正叔,学者称为伊川先生。北宋哲学家、教育家。曾与其兄程颢学于周敦颐,并同为北宋理学的奠基者,世称“二程”。讲学达三十余年,其学以“穷理”为主。

    M98调用子程序是什幺意思?它的含义是什幺?什幺情况下才能用到这代码?请高人详细指点下,感激不尽。

    M98代码的含义是调用子程序,格式是:

    M98 P _ _ _ _ _ _ _ ;

    P 后面最多可写7位数,后4位为子程序号,前三位为调用次数。

    当一个程序中有若干完全重复的程序段时,可将这些程序段提出来,另编成一个程序,用M99结束,作为子程序。原程序为它的主程序。

    主程序运行到需要子程序内容的地方,用M98 P______;调用子程序运行。子程序结束时会回到主程序调用的程序段处,继续向下运行。

    可用子程序的情况太多了,一个主程序往往有许多子程序。

    打了不少字了,几句话说不清楚,看系统的说明书,认字的人就能看明白。

    有不明白的地方,多交流。

    你理解的对,装夹多工件同时加工、相同的加工轨迹使用子程序可简化程序。

    平面轮廓使用不同刀补完成粗、半精、精加工。

    分层多次下刀完成较深平面轮廓的加工。

    一组相同的孔多次换刀加工,孔位坐标可用子程序。

    等等。例子太多了。

    如能灵活应用,可用的地方太多了!在工件加工中,一个主程序可能用许多子程序。

    我干加工中心20多年,可以互相交流使用经验。

    如在同一平面上对不同位置的三个孔进行精铣:(FANUC)

    主程序

    O1000

    N10

    N20

    :

    :

    N60G90G00X-200Y0(1孔位)

    N70M98P1001

    N80G90G00X0Y0 (2孔位)

    N90M98P1001

    N100G90X200Y0 (3孔位)

    N101M98P1001

    :

    :

    N500M30

    子程序

    O1001

    N10G91G00Z-25

    N20G01G41X25Y-25D01F100

    N30G02X25Y25R25

    N40I-50

    N50X-25Y25R25

    N60G01G40X-25Y-25

    N70G00Z25

    M99

    数控车床子程序是什幺意思?

    1子程序的定义

    在编制加工程序中有时会遇到一组程序段在-个程序中多次出现或者在几个程序中都要使用它。这个典型的加工程序可以做成固定程序并单独加以命名这组程序段就称为子程序。

    2.使用子程序的目的和作用

    使用子程序可以减少不必要的编程重复从而达到减化编程的目的。主程序可以调用子程序一个子程序也可以调用下一级的子程序。子程序必须在主程序结束指令后建立其作用相当于一个固定循环。

    3子程序的调用

    在主程序中调用子程序的指令是一个程序段其格式随具体的数控系统而定FANUC-6T

    系统子程序调用格式为

    M98 P--L--

    式中 M98--子程序调用字

    p--子程序号

    L--子程序重复调用次数。

    由此可见子程序由程序调用字、子程序号和调用次数组成。

    4子程序的返回

    子程序返回主程序用指令M99它表示子程序运行结束请返回到主程序。

    5子程序的嵌套

    子程序调用下一级子程序称为嵌套。上一级子程序与下一级于程序的关系与主程序与第一层子程序的关系相同。子程序可以嵌套多少层由具体的数控系统决定在FANUC-6T系统中只能有两次嵌套。

    在c/c++中什幺是程序的模块、接口和子程序,分别都是什幺意思,如果有实例就更好了,谢谢

    程序的模块是指:例如整个项目程序实现一个打飞机的功能,这个功能分很多小功能(你的飞机开炮,移动; 敌人的飞机开炮,移动),就可以说敌人飞机实现代码所以部分叫成程序的一个模块

    子程序:就是指代码函数实现具体功能的函数,其中一个小函数实现飞机开炮功能就可以叫子程序,而主程序就是调用所有子程序的集合

    接口就是指:比如你这个程序中,有写一个子程序,而其他人也想做类似打飞机的游戏,想要你这个子程序代码,他要写吗?不用,你就写一个接口函数,开放给他用,然后他就通过调用你这个开放的接口,他也能实现你这个打飞机其中一个小功能

    易语言&子程序名是什幺意思

    子程序名()是表示直接跳到该子程序中继续运行代码。

    &子程序名 通常是用作某个命令调用来使用的。在命令里面需要使用子程序的话,就可以用这样的方法来获取子程序指针

    中断是什幺意思,和子程序的区别呢

    中断程序是在中断事件发生时调用,程序也不知道什幺时候有中断事件发生。子程序是在需要的时候调用,在程序里可以预见它的使用。

    因为中断是由系统调用的,不知道什幺时候中断,所以你可以选择允许中断或者不允许中断,这就好比你正在干工作时,电话铃响了,你可以选择接电话也可以选择不接电话,中断就好比电话铃,你不知道电话铃什幺时候响起。开放中断就是允许中断,就好比电话铃响允许你接电话,禁止中断就是不允许中断,就好比电话铃响不允许你接电话。子程序是条件调用的程序,你可以知道什幺时候调用,子程序是为了优化程序结构。

    简单一句,中断是随时发生的,时间不确定。而子程序则时间确定,跟系统设计顺序有关系,起简化、优化作用。用在随机事件,只是中断的一个应用。

    有些中断的应用,其事件是可以预料,也能确定的,比如说定时。

    子程序控制的内容与主程序是一样的,都是在扫描周期结束后才变化的,而中断就是马上去做,不等这个扫描周期结束 。

    中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

    计算机科学术语。指处理机处理程序运行中出现的紧急事件的整个过程。

    程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行,这整个过程称为程序中断;当处理机接受中断时,只需暂停一个或几个周期而不执行处理程序的中断,称为简单中断。

    中断又可分为屏蔽中断和非屏蔽中断两类。可由程序控制其屏蔽的中断称为屏蔽中断或可屏蔽中断。屏蔽时,处理机将不接受中断。反之,不能由程序控制其屏蔽,处理机一定要立即处理的中断称为非屏蔽中断或不可屏蔽中断。

    非屏蔽中断主要用于断电、电源故障等必须立即处理的情况。处理机响应中断时,不需执行查询程序。由被响应中断源向CPU发向量地址的中断称为向量中断,反之为非向量中断。向量中断可以提高中断响应速度。

    参考资料

    编辑委员会.数学辞海.北京:中国科学技术出版社,2002

    子程序返回值是什幺意思,起什幺作用

    调用格式: 〈整数型〉 取字节集长度 (字节集 字节集数据) - 系统核心支持库->字节集操作

    英文名称:BinLen

    取字节集型数据的长度。本命令为初级命令。

    参数<1>的名称为“字节集数据”,类型为“字节集(bin)”。参数值指定欲检查其长度的字节集数据。

    红字,是返回值的数据类型:整数型,那幺他返回的就一定是123456~~~~~这些数字

    粗体,是返回值的意义:取字节集型数据的长度,那幺这个子程序的返回值,就是一个数据的长度。

    按键精灵中,子程序是什幺意思?

    语法:

    Sub 子程序

    内容

    End Sub

    ----------------------

    调用:Call 子程序

    ------------------------

    子程序的意思,子程序如一个箱子,你可以往里面安放各种代码,当你要用到里面的代码的时候,你就可以调用子程序,执行里面的代码。子程序一般是用在某段代码应用较多的时候,反复输代码太麻烦,不如存进一个子程序,想用的时候一句调用就可以了

    展开全文
  • 实现这一步骤的必要性在于:一旦中断服务程序驻留内存后,一般程序员使用这一新增的中断调用就如同调用DOS或BIOS的中断子程序一样,只要了解其入口要求和返回参数就可调用。程序驻留在内存后,它占用的存储区就不会...
  • 在学习子程序的过程中,看了整个的ppt,也没搞明白一个带有子程序的汇编程序长什么样。 我们先看一个例子,然后分析。 datas segment w dw 10 h dw 6 turns dw 30 start_x dw 20 start_y dw 20 datas ends ...

    在学习子程序的过程中,看了整个的ppt,也没搞明白一个带有子程序的汇编程序长什么样。
    我们先看一个例子,然后分析。

    datas segment
    	w       dw 10
    	h       dw 6
    	turns   dw 30
    	start_x dw 20
    	start_y dw 20
    datas ends
    
    stack segment stack
    	      dw 32 dup(?)
    stack ends
    
    code segment
    	        assume cs:code,ds:datas,ss:stack
    main proc near
    	start:  
    	        mov    ax,datas
    	        mov    ds,ax
    	        mov    ax,stack
    	        mov    ss,ax
    
    	        mov    ah,0
    	        mov    al,4                     	;图像模式
    	        int    10h
    	        mov    cx,turns
    	loops:  
    	        call   hor_ver
    	        mov    ah,0
    	        int    16h
    	        loop   loops
    	        mov    ah,0                     	;文本模式
    	        mov    al,3
    	        int    10h
    
    	        mov    ah,4ch
    	        int    21h
    main endp
    	;打印一个楼梯
    hor_ver proc near
    	        push   ax
    	        push   bx
    	        push   cx
    	        push   dx
    
    	        mov    cx,w
    	li:     
    	        push   cx
    	        mov    cx,start_x
    	        mov    dx,start_y
    	        mov    al,1
    	        mov    ah,0ch
    	        int    10h
    	        inc    start_x
    	        pop    cx
    	        loop   li
    
    	        mov    cx,h
    	ld:     
    	        push   cx                       	;写入像素点
    	        mov    cx,start_x               	;x坐标
    	        mov    dx,start_y               	;y坐标
    	        mov    al,1                     	;像素值
    	        mov    ah,0ch
    	        int    10h
    	        inc    start_y                  	;y++ 打印竖
    	        pop    cx
    	        loop   ld
    
    	        pop    dx
    	        pop    cx
    	        pop    bx
    	        pop    ax
    	        ret
    hor_ver endp
    code ends
    end start
    

    基本结构

    功能是像素点打印楼梯,随便找的例子没别的意思。
    和一般的程序差不多,都是三段segment,code段有assume和数据段绑定。
    (如果不是很清楚一个汇编程序的格式,看这篇博客

    两个代码块

    不过我们的code段中,出现了两个部分:
    main proc near
    ……
    main endp

    hor_ver proc near
    ……
    hor_ver endp

    这两部分就有一点像C的主函数和子函数,我们按照命名就能看出来具体谁是谁了。
    一个程序,需要有一个名字(废话),这个是要写在proc和endp前面的,所以一个简单的子程序/或者主程序是这样的:

    **proc
    
    **endp
    

    至于这两个谁在上面都无所谓。

    near?

    但那个near是什么:

    NEAR属性(段内近调用)的过程只能被相同代码段的其他程序调用。
    FAR属性(段间远调用)的过程可以被相同或不同代码段的程序调用。

    如果是有多个代码段,那么我们一定需要far,但如果是段内调用,还是选择near,因为near相对较快

    有两个建议:

    1. 首先如果是段内调用,其实near还是far的都可以不写……,但是还是写了比较好
    2. 主程序部分建议定义为far类型,便于返回dos,也就是将我们的主程序

    这时,我们的代码长这样:

    **proc near	;缺省就是near
    
    **endp
    

    压栈出栈?

    为什么调用子程序还需要压栈出栈啊?
    因为我们只有这几个寄存器,如果主程序和子程序调用了同一个,如果不保存,那么我们主函数的内容不就没了?(专业一点,这叫保护现场)
    这里我们保存的方式还是有几种类型的:

    1. 压栈出栈,这个是比较简单的。如果使用堆栈法,那么保护恢复一定要在同一个地方
      (子程序或者主程序,不能分成两部分)
    2. 存内存,也就是在data数据段中定义变量来存储。

    当然了,如果不想管,直接就将四个寄存器都压栈,然后反过来都出栈了就行,

    ret&call

    ret就是return的缩写,也就是函数的返回部分。
    看着似乎没什么,其实本质上人家可是干了很多事。
    在调用子程序的部分,我们的cs段寄存器和ip寄存器记录了当前指令的位置,如果有在dos尝试单步调试会发现,在函数返回的过程中,IP寄存器的值是会发生改变的。
    所以ret其实就是jmp+pop的操作。

    那我们怎么调用?
    可以看到,上面的程序中有一个call指令,后面跟着我们的子程序名称,这就是调用了。
    call和ret一个将ip寄存器压栈,一个弹出,保证了我们的代码实现。

    其实call规范来说是这样的:
    call near/far ptr 函数名,如果是far调用需要将cs和ip都进行压栈。
    但是我们还是如果缺省就默认为near。

    ret的一个骚操作

    ret [val],其中val一定是一个正偶数

    啥?是返回值吗?

    不是,我们在rer的过程中,我们有一个将ip出栈的操作,正常来说sp指针加2就行了,但是如果是有这个数值,那么我们还需要将sp再加上这个正偶数
    在这里插入图片描述
    因为是dw类型的堆栈,所以我们的val才需要是正偶数。
    另外强调一点,栈顶在高位,也就是我们压栈其实是从高往低。

    (可这nm有什么用吗?
    比如在子程序中我们将一些数据进行了压栈处理的,但是后面就用不上了,这里我们就可以直接ret,不需要一个个出栈了。
    虽然只是修改了指针,而不是出栈,但当我们继续压栈的过程中,是会将数据覆盖掉的,所以还是没有影响的。

    start写在哪?

    当时就这个头疼了半天。
    我们想一下,start是开始程序的标志,汇编和C什么的差不多,都是串行结构,说白了就是一条条执行,所以C从主函数起步,在这里我们也是这样。
    start写在主程序的开始,start end在代码段结束。

    另外我们的assume是一切代码的前提,只有绑定了才能找到位置,所以assume在code 段的最开始

    参数传递问题

    子程序不可能都没有返回值吧,也不可能都没有参数传入吧。
    这里我们给几个方法:

    • 寄存器法
    • 变量法(存内存)
    • 堆栈法
    • 参数地址指针法

    寄存器法

    就是将传入传出的参数放在寄存器里面。
    主程序:传入不用管,传出视情况保护;
    子程序:传入建议保护,传出不能保护和恢复。(保护的不是返回值,而是之前的值,如果恢复了会将返回值覆盖掉!)

    堆栈法

    就是将传入和传出的参数都压入堆栈中。
    因为保存和恢复也在堆栈中,如果处理不好直接翻车,但是能省下内存和寄存器,优缺点都很明显。

    比如我们是将传入和传出的参数都放在CX寄存器中,在子程序将ABCX三个寄存器压栈。
    主程序调用:

    PUSH	CX			;堆栈传递入口参数
    CALL  	SQROOT	
    POP 	CX			;堆栈返回运算结果 
    

    我们一步步来分析:

    1. 刚进入子程序,参数在栈中,同时我们的返回地址也在(call指令干的活)
    2. 将三个寄存器压栈
    3. 我们需要将传入参数放入CX寄存器中,需要将sp指针加8,然后pop到cx中
    4. 计算返回值(在CX中),将CX压栈(就是那个平方根,白嫖的图)。
    5. 将sp-8,调整为压栈之前的样子
    6. 寄存器出栈,ret返回,这样在主程序中我们的栈顶元素就是返回的参数了。

    在这里插入图片描述
    在这里插入图片描述
    这里我们修改了sp的值,即直接修改栈内指针位置,如果子程序中还需要使用栈,解决办法:

    1. 继续调整sp指针,不过很麻烦还容易出错;
    2. 将我们的cx寄存器赋值(传入参数)改为寻址方式: MOV CX, [BP+……],这样会好很多。

    参数地址指针法

    上面的方式传一个两个数据都行,但如果是多个数据(比如一个数组),如果还是采用之前的方式,尤其是堆栈法,算地址直接爆炸。
    所以我们将数组的首地址在主程序中赋值到SI寄存器,将个数赋值到CX寄存器(习惯,不是强行要求的),在子程序直接使用就行,注意子程序中的SI和CX要保护一下。

    和寄存器法其实差不多,
    注意传数组地址要使用lea指令,非要offset也行,但是这个能叼一点,
    另外数组中的元素直接使用[SI]就是了,如果是dw类型,si一次加2,db加1(指元素后移)。

    子程序的嵌套问题

    就是套娃,比如我们在一个子程序中再调用另一个子程序。

    我们要注意,每一次调用都涉及到压栈出栈,即使没有寄存器压栈,也有ip压栈,所以堆栈空间一定要够用,然后就是call 和 ret 一定要玩明白,要保护好寄存器。

    主要的部分我们想讲一讲递归的问题,这里以Fibonacci为例。
    如果是递归的程序,那么参数传递就一定要是寄存器法或者堆栈法,除非一个递归函数只能延申出一个递归部分,如计算n!,我们调用的过程中函数个数线性增加,否则使用参数法就会爆炸。

    和一般的一样,我们也需要有边界条件:
    在Fibonacci中,F(1) = F(2) = 1。
    剩下的F(n) = F(n-1)+F(n-2)。
    (可以想象出,如果我们直接写到最后一层,一共需要传递很多个参数,所以还是堆栈法和寄存器法好一些;另外如果n足够大,堆栈也容易炸掉。)

    功能:输入一个不限位数的十进制数,回车后打印fibo的结果。
    (可以输入个位数、十位数,非要输入一个百位的也行吧,反正自己电脑受得了再加上bl寄存器大小受得了就行)

    DATAS SEGMENT
        
    DATAS ENDS
    
    STACKS SEGMENT
    	a      dw 20 dup(?)
    STACKS ENDS
    
    CODES SEGMENT
    	        ASSUME CS:CODES,DS:DATAS,SS:STACKS
    
    fibo proc
    	;读入al的数据,计算,返回在ax中
    	        push   bx
    	        push   cx
    	        push   dx
    
    	        cmp    al, 1h
    	        je     get_out
    	        cmp    al, 2h
    	        je     get_out                    	;if(n == 1 || n == 2) return 1;
    
    	        mov    dl, al
    	        sub    al, 1h
    	        call   fibo
    	        mov    bx,ax                      	;fibo(n-1)
    
    	        mov    al,dl
    	        sub    al, 2h
    	        call   fibo                       	;fibo(n-2)
    	        mov    cx,ax
    
    	        mov    ax,bx
    	        add    ax,cx
    
    	        pop    dx
    	        pop    cx
    	        pop    bx
    	        ret
    
    	get_out:
    	        mov    ax, 1h
    
    	        pop    dx
    	        pop    cx
    	        pop    bx
    	        ret
    fibo endp
    	;打印回车
    dpcrlf proc                               		;过程开始
    	        push   ax                         	;保护寄存器AX和DX
    	        push   dx
    	        mov    dl,0dh                     	;显示回车
    	        mov    ah,2
    	        int    21h
    	        mov    dl,0ah                     	;显示换行
    	        mov    ah,2
    	        int    21h
    	        pop    dx                         	;恢复寄存器DX和AX
    	        pop    ax
    	        ret                               	;子程序返回
    dpcrlf endp
    	;打印空格
    printf PROC
    	        push   ax
    	        push   cx
    	        push   dx
    	        mov    cl,20h
    	        mov    dl,cl
    	        mov    ah,2h
    	        int    21h
    	        pop    dx
    	        pop    cx
    	        pop    ax
    	        ret
    printf ENDP
    	;将ax中的16位内容按十进制打印
    hex PROC
    	        PUSH   AX
    	        PUSH   BX
    	        PUSH   CX
    	        PUSH   DX
    	        PUSHF
    	        XOR    CX,CX
    	        MOV    BX,10
    @DSPAX1:
    	        XOR    DX,DX
    	        DIV    BX
    	        INC    CX
    	        OR     DX,30H
    	        PUSH   DX
    	        CMP    AX,0
    	        JNE    @DSPAX1
    	        MOV    AH,2
    @DISPAX2:
    	        POP    DX
    	        INT    21H
    	        LOOP   @DISPAX2
    	        MOV    DL,32
    	        INT    21H
    	        POPF
    	        POP    DX
    	        POP    CX
    	        POP    BX
    	        POP    AX
    	        RET
    hex ENDP
    main proc
    	START:  
    	        MOV    AX,DATAS
    	        MOV    DS,AX
    	        xor    bl,bl
    	input:  
    	        mov    ah,1h
    	        int    21H
    	        cmp    al,13                      	;换行
    	        jz     next                       	;相等跳出
    	        sub    al,30h
    	        mov    dl,bl
    	        mov    cl,3h
    	        sal    bl,cl
    	        sal    dl,1                       	;10
    	        add    bl,dl                      	;存储在bl中
    	        add    bl,al
    	        jmp    input
    
    	next:   
    	        mov    al,bl
    	        call   fibo
    	        ;call   printf
    	        call   hex
    	        MOV    AH,4CH
    	        INT    21H
    main endp
    CODES ENDS
        END START
    

    第一个函数是计算Fibonacci的,按照递归的定义来就好了,别忘了两个分支上都要有出栈和ret
    然后中间的间隔方式我给出了两种,一个是换行回车,一个是空格,这里调用了换行。(都是中断调用,没什么可讲的)
    我们的十进制转换采用的是除法+压栈实现的。
    实现不限位数的输入我们是采用判断回车来实现的,如果没有读到回车就将bl的内容乘十然后加上我们刚刚读入的内容。

    展开全文
  • 中断处理程序中断服务例程

    千次阅读 2017-07-20 11:27:21
    1 什么是中断2中断处理程序3中断服务例程4request_irq函数分析 1. 什么是中断 简单来说中断就是硬件设备与处理器的一种交流方式,比如当我按下一个键时,只有当处理器知道我按下了这个键并且做出相应...
  • 西门子PLC调用子程序的方法

    千次阅读 2020-12-19 13:41:05
    调用西门子plc的例行程序(CALL)指令将控制转换给例行程序(SBR_n)。您可以使用带参数或不带参数的“调用例行程序”指令。在例行程序完成执行后,控制返回至“调用例行程序”之后的指令。每个例行程序调用...
  • 中断服务程序实例

    千次阅读 2009-09-12 15:23:00
    以下是一个统计中断时间间隔的中断服务程序。irqreturn_t short_interrupt(int irq, void *dev_id, struct pt_regs *regs) { static long mytime=0; static int i=0; struct net_device *dev=
  • 中断时计算机科学中最基本、也是十分重要的一个概念...定义:中断就是使CPU暂时挂起当前正在进行的工作并转向某紧急事件的服务与处理程序(该服务与处理程序称为中断服务程序),在执行完中断服务程序后再返回到被中止
  • 8086中断处理程序分析

    千次阅读 2017-09-02 14:50:41
    中断处理程序的入口地址,即中断向量必须存储在对应的中断向量表表项中,通俗的讲就是中断处理程序的地址放在一块内存中,这块存储中断处理程序的入口的地址空间叫中断向量表,而实际地址就叫中断向量
  • 中断向量 中断源的识别标志,可用来形成相应的中断服务程序的入口地址或存放中断服务程序的首地址。运行过程 在中断响应过程中,CPU通过从接口电路获取的...图三 设置中断服务函数的底层库函数举例 上图是利用山外
  • 搞清了i386 CPU的中断机制和内核中有关的初始化以后,我们就可以从中断请求的发生到CPU的响应,再到中断服务程序的调用与返回,沿着CPU所经历的路线走一遍。这样,既可以弄清和理解linux内核对中断响应和服务的总体...
  • python循环定时中断执行某一段程序...这样子每隔一段时间执行一次中断当然不能用sleep,这样子整个进程就停在这了,而不是接着爬数据解决方法用到threading的Timer,也类似单片机那样子,在中断程序中再重置定时器,...
  • 硬件中断,软件中断

    千次阅读 2020-08-10 09:54:21
    相比较之下,硬件触发更具优势,软件触发需要程序控制(软件触发需要程序调用cpu去采集信息并判断是否满足触发条件)。 硬件触发:数据采集卡被动等待触发信号,接收到信号后才进行数据采集;触发信号可由某个仪器...
  • C51常用汇编子程序

    2010-05-09 10:50:23
    C51常用汇编子程序:ACM-12864汉字液晶显示驱动程序,定时与中断类,Ad转换类,排序类,频率计,串行通信,代码运算类,DES算法演示,C51_table,I2c总线,IIC,键盘与显示类,交通灯,步进电机,电机控制,等等
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼/*************************************************************************************** 外部中断0实验 *实现现象:下载程序后按下K3按键可以对D1小灯状态取反。...
  • KUKA机器人程序中的中断-interrupt

    万次阅读 2020-07-24 09:59:17
    中断-- kuka机器人一个使用不多但却又不得不用的功能 ... 由中断而调用的子程序被称为中断程序。 最多同时允许声明 32 个中断。 在同一个时间最多允许有 16 个中断激活。 注意中断...
  • 子程序调用和中断指令

    千次阅读 2007-05-20 17:25:00
    子程序的调用及返回指令1.调用call dst;调用的时候ip或eip压栈将机器执行的当前值(即call指令的下一条指令的地址)压入堆栈以便子程序返回用。2.返回ret返回时ip出栈带参数的返回指令 ret NN是一个16位的常数(偶数...
  • 外部中断

    千次阅读 2020-03-01 17:28:35
    外部中断配置与程序举例 一.外部中断输入引脚 外部中断0(INT0):P3.2口的附加功能 外部中断1(INT1):P3.3口的附加功能 当引脚上出现有效的中断信号时,就有中断标志置1,向CPU申请中断。 二.外部中断相关寄存器...
  • 中断系统8.1 中断的基本概念8.1.1 中断概念的引入及描述中断方式示意(以输入中断为例)**中断**的定义8.1.2 中断源及中断分类中断的分类8.1.3 中断...处理过程中断向量的引导作用中断服务子程序的结构8.2 多级中断管理1....
  • 子程序描述 名称:dtoc 功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。 参数:(ax)=word型数据 ds:si指向字符串的首地址 返回:无 应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,...
  • 本着共同学习,共同进步的目的,把我搞到的资料不辞辛苦的发上来,供初学者参考...1.流水灯C程序#include ////////////////////////////////////////////////////////void delay(unsigned int cnt) //简单的延时{wh...
  • 中断、软中断和信号

    万次阅读 2016-08-04 14:04:26
    基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。 2. 处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行...
  • 汇编语言:简单的子程序设计

    千次阅读 2016-12-15 16:26:03
    汇编语言:简单的子程序设计 本文内容来源于王爽《汇编语言(第三版)》实验10 编写子程序 这次要求编写三个子程序,所有思路提示在书中给出了,需要了解的同学可以到书中寻找相关提示(尽管网上有电子书,但我强烈...
  • PAGEPAGE 290第13章 几个C语言编程应用实例13.1 TMS320LF2407与图形液晶显示模块接口及应用源程序代码:所需的复位和中断向量定义文件“vectors.asm”.title "vectors.asm".ref _c_int0,_nothing.sect ".vectors...
  • 硬件中断与软件中断

    万次阅读 多人点赞 2018-08-05 23:26:52
    每个设备或设备集都有他自己的IRQ(中断请求), 基于IRQ, CPU可以将相应的请求分发到相应的硬件驱动上(注: 硬件驱动通常是内核中的一个子程序, 而不是一个独立的进程).处理中断的驱动是需要运行在CPU上的, 因此, 当...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,914
精华内容 10,765
关键字:

中断服务子程序举例