汇编语言中中断INT和子程序调用CALL保护现场时分别压入堆栈的是什么

汇编语言中中断INT和子程序调用CALL保护现场时分别压入堆栈的是什么,第1张

中断INT过程:

1取得中断类型码

2把标志位压入栈中

3把CS压入栈中

4把IP压入栈中

5更改CS和IP,转到中断程序

CALL:将当前IP或者CS和IP压入栈中,到底是把IP还是IP和CS压入栈中。就要看CALL后到底是一个字还是2个字的,如果是一个字的,就只把IP压入栈中,例如CALL AX;如果是两个字,就先把CS压入有栈中,再把IP压入栈中,例如CALL dword ptr 内存单元地址

希望对你有帮助!

;

ORG 0000H

LJMP MAIN;上电,转向主程序

ORG 000BH

LJMP CTC0;转向定时器中断程序

MAIN:

MOV TMOD, #01H

MOV TL0, #0E0H

MOV TH0, #0D8H ;定时10ms, T0初始值计算得D8E0H

SETB TR0 ;启动定时器T0

SETB ET0 ;允许T0中断

SETB EA ;CPU开放中断

MOV R1, #0

MOV R2, #0A0H ;设循环次数,循环160次

MOV P1, #0C0H ;数字显示

wait:

AJMP wait

CTC0: ;定时器0中断子程序

MOV TL0, #0E0H

MOV TH0, #0D8H

DJNZ R2, EXIT

MOV R2, #160

INC R1

CJNE R1, #10, Display ;判断是否到达10

MOV R1, #0

Display:

MOV A, R1

MOV DPTR, #TAB

MOVC A, @A + DPTR ;查表

MOV P1, A

EXIT:

RETI

TAB:

DB 0C0H, 0F9H, 0A4H, 0B0H, 99H, 92H, 82H, 0F8H

DB 80H, 90H, 88H, 83H, 0C6H, 0A1H, 86H, 08EH

END

试试看。

品牌型号:华为MateBook D15

系统:Windows 11

中断服务子程序与普通子程序的主体不同、功能不同、特点不同。

1、主体不同:中断服务子程序是一种服务,是通过执行事先编好的某个特定的程序来完成的。普通子程序是一个大型程序中的某部份代码,由一个或多个语句块组成。

2、功能不同:中断服务子程序,外界发生了紧急情况,要求CPU暂停当前的工作转去处理这个紧急事件。处理完毕后,再回到原来被中断的地址,继续原来的工作。普通子程序,负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

3、特点不同:中断服务子程序,为了在中断处理结束后能够使进程准确地返回到中断点,系统必须保存当前处理机程序状态字PSW和程序计数器PC等的值。普通子程序,常被使用在汇编语言层级上。子程序的主体是一个代码区块,当被调用时就会进入运行。

如果是用C去写的话,像汇编一样,开中断就行了。位置一般就在MAIN 之前的。

//晶振频率221184MHz

#include<at89x52h>

#define TIMER0H 0x4c

#define TIMER0L 0x00

#define TIMER0_RUN TR0=1

#define SECOND_OVERFLOW 40

#define SEG_PORT P0

#define DISPLAY_DIG1 P1&=0xf0;P1|=0x01

unsigned char g_CurrentDigit=0; //当前显示的数字

void timer() interrupt 1

{

static unsigned char s_Count = 0;

TH0 = TIMER0H; //重置定时器初值

TL0 = TIMER0L;

//每次进入中断服务程序,TH0和 TL0 的值都

TIMER0_RUN; //定时器运行,开始下一

if(s_Count != SECOND_OVERFLOW)

{ //未到整秒,把 sCount 值加1

s_Count++;

}

else

{ //到整秒,s_Count归 0,更新把当前显示

s_Count = 0;

if(g_CurrentDigit != 9)

{

g_CurrentDigit++;

}

else

{

g_CurrentDigit = 0;

}

}

return;

}

void Initial(void) //初始化

{

IE = 0x82; //仅允许Timer0 中断

TMOD = 0x01; //Timer0 使用工作方式 1(16位) ,定时器

TH0 = TIMER0H; //设置定时器初值

TL0 = TIMER0L;

TIMER0_RUN; //定时器开始运行

DISPLAY_DIG1;

}

void main()

{

unsigned char code SEG_CODE[]

= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

Initial();

while(1)

{

SEG_PORT = SEG_CODE[g_CurrentDigit]; //显示当前的数字

//当timer0溢出时,单片机响应 timer0 中断,调用 timer 函数,

//每40 次调用当前显示的数字加 1

}

}

程序分析:

程序中主程序做的事只是在死循环中反复显示当前的数字,每产生一次中断,程序就跳

转到中断服务函数 timer()中进行相应的更新。

这里中断服务函数 timer()有别于普通 C函数的地方是在声明中多了“interrupt 1” ,说明

这个函数是中断号为 1的中断服务函数。各个中断对应的中断号如表 38所示。

这个程序需要初始化的东西比较多, 我们把这些初始化语句都放在了初始化函数 Initial()

中,这也是程序初始化很常见的做法。我们还第一次用到了静态变量和全局变量。全局变量

是中断处理函数与外界程序进行参数传递的唯一途径,因此在单片机程序中全局变量的使

用频率要比普通的 C 语言程序高。尽管如此,由于全局变量的使用会影响程序的结构化,

所以在可以不使用全局变量的地方,还是要避免使用全局变量。在程序中,为了把全局变

量与静态变量跟普通变量区别开来,我们在变量前分别加了小写 g_和小写 s_以示区别。

IE 寄存器中的使能位和C中的中断号 中断源

0 外部中断0

1 定时器0溢出

2 外部中断1

3 定时器1溢出

4 串行口中断

5

定时器2溢出(仅在S52、

C52中有此中断源)

好了,以上是我找的一个实例,希望对你有帮助!

主程序:

push ds ;保存数据段

mov ax,0000

mov ds,ax ;数据段清零

mov ax,offset irq7 ;取中断程序入口地址

add ax,2000 ;加装时IP=2000地址

mov si,003c ;填8259中断7中断矢量

mov w[si],ax ;填偏移量矢量

mov ax,0000 ;段地址CS=0000H

mov si,003e

mov w[si],ax ;填段地址矢量

pop ds ;d栈

in al,21 ;读8259中断屏蔽字

and al,7f ;开8259中断7

out 21,al

mov al,b4 ;8253的计数器2为方式2,采用二进制计数,先低后高写入计数值 10110100

out 43,al ;写入方式控制字

mov ax,2e9c 0010 1110 1001 1100B 11932D

out 42,al ;写入低字节计数值 1001 1100

mov al,ah

out 42,al ;写入高字节计数值 0010 1110

mov al,81 ;8255的A口为方式0输出,B口为方式0输出,C口下部输入 1000 0001

out 63,al ;写方式控制字

call first ;调用first子程序,赋计数初值

begi:hlt 延时等待

sti ;开中断

mov ah,01

int 16 ;检测是否按了键盘

jz begi

mov ah,00 ;读键值

int 16

cmp al,0d ;是否按了回车

jnz A1

mov si,4000

not [si+04] ;偏移地址为4004的内存单元内容取反

jmp begi

A1:cmp al,1b ;是否按了ESC键

jnz A2

call first ;重新赋初值,相当于清零

A2:jmp begi

中断程序:

irq7:call disp ;调用disp子程序,用来在数码管显示数据

mov si,4000

cmp [si+04],00 ;判断是否按了第2次回车键

je A4

call addn ;调用addn子程序,用来计数

A4:mov al,20

out 20,al

cli ;关中断

iret ;返回

addn程序:

addn:mov si,4000

add [si+03],01 ;百分之一秒加1

cmp [si+03],0a ;判断是否大于10

jz A5

jmp A9

A5:mov [si+03],00

Add [si+02],01 ;十分之一秒加1

cmp [si+02],0a ;判断是否大于10

jz A6

jmp A9

A6:mov [si+02],00

add [si+01],01 ;秒位加1

cmp [si+01],0a ;判断是否大于10

jz A7

jmp A9

A7:mov [si+01],00

add [si],01 ;十秒位加1

cmp [si],06 ;判断是否大于6

jz A8

jmp A9

A8:mov [si],00 ;大于60:00重新开始

A9: ret

赋初值程序:

first:mov si,4000

mov al,00

mov [si],al

mov [si+01],al

mov [si+02],al

mov [si+03],al

mov [si+04],al

ret

显示程序:

disp:push ax ;保存AX

mov si,4000 ;指向数据缓冲区

mov dl,f7 ;1111 0111 指向数码管

mov al,dl ;al=1111 0111

again:out 60,al ;写端口A

mov al,[si]

mov bx,4100 ;指向数码缓冲区 bx=0100 0001 0000 0000

and ax,00ff ; ax=0000 0000 al

add bx,ax ;得到显示代码 bx=0100 0001 al

mov al,[bx]

out 61,al ;写端口B

call dally :调用延时程序dally

inc si

mov al,dl

test al,01

jz out

ror al,1 ;指向下一个数码管

mov dl,al

jmp again

out: pop ax ;d出AX

ret

dally:push cx ;延时程序

push ax

mov cx,0010

t1 :mov ax,0010

t2 dec ax

jnz t2

loop t1

pop ax

pop cx

ret

数码缓冲区:

0000:4000 3f,06,5b4f,66,6d,7d,07,7f,6f

二、 设计思想

电子秒表要实现的功能:用键盘中断来控制整个程序,按一下回车键启动电子秒表,再按一下暂停,按一下ESC键清零,用七段数码管显示时间。整个程序涉及到8255、8253与8259三个芯片。8253的OUT2,CLK2分别连接8259的IRQ7与PCLK,8253的GATE2连接正5伏电压,采用计数器2每隔001秒产生一次中断并且计数,写入以偏移地址4000H开始的4个内存单元,然后利用8255将内存单元的数据输出到七段数码管。由于键盘中断优先于8259的7号中断,所以程序只有在按一下回车键才启动电子秒表,再按一下暂停,按一下ESC键清零,如果超出了60秒,整个程序自动重新开始。

三、 所用芯片工作原理

8255:接口电路在CPU和I/O设备之间起着信号的变换和传输的作用。 任何接口电路与CPU之间的信息传输方式都是并行的,即CPU与接口电路之间以数据字节/字为单位传送信息。接口电路与I/O设备之间的信息传送方式,有并行和串行两种,相应的接口电路称为并行接口和串行接口。

并行接口是在多根数据线上,以数据字节/字与I/O设备交换信息。在输入过程中,输入设备把数据送给接口,并且使状态线“输入准备好”有效。接口把数据存放在“输入缓冲寄存器”中,同时使“输入回答”线有效,作为对外设的响应。外设在收到这个回答信号后,就撤消数据和“输入准备好”信号。数据到达接口中后,接口会在“状态寄存器”中设置输入准备好标志,或者向CPU发一个中断请求。CPU可用查询方式或中断方式从接口中读取数据。接口中的数据被读取后,接口会自动清除状态寄存器中的标志,且撤消对CPU的中断请求。

在输出过程中,每当输出寄存器可以接收数据,接口就会将状态寄存器中“输出准备好”状态置1或向CPU发一个中断请求,CPU可用查询或中断方式向接口输出数据。当CPU输出的数据到达接口后,接口会清除“输出准备好”状态,把数据送往外设,并向外设发一个“数据输出准备好”信号。外设受到驱动后,便接收数据,并向接口电路发一个“输出回答”信号,接口收到该回答信号后,又将状态寄存器中“输出准备好”置位,以便CPU输出下一个数据。

8253:对CLK信号进行“减1计数”。首先CPU把“控制字”,写入“控制寄存器”,把“计数初始值”写入“初值寄存器”,然后, 定时/计数器按控制字要求计数。计数从“计数初始值 开始,每当CLK信号出现一次,计数值减1,当计数值减为0时,从OUT端输出规定的信号(具体形式与工作模式有关)。当CLK信号出现时,计数值是否减1(即是否计数),受到“门控信号”GATE的影响,一般,仅当GATE有效时,才减1门控信号GATE如何影响计数 *** 作,以及输出端OUT在各种情况下输出的信号形式与定时/计数器的工作模式有关。

8259:1 IR线上提出了中断请求的中断源,即出现请求,IRR中断请求寄存器(共有8位D7~D0)对应于连接在IR0~IR7线上的外设的中断请求,哪一根输入线有请求,哪一根输入线就置1。

2 若OCW1(IMR中断屏蔽寄存器)未使该中断请求屏蔽(对应位为0时不屏蔽),该请求被送入PR(优先权分析器)比较。否则,不送入PR比较。

3 PR把新进入的请求与ISR(服务中寄存器)中正在被处理的中断进行比较。如果新进入的请求优先级较低,则8259不向CPU提出请求。如果新进入的请求优先级较高,则8259使INT引脚输出高电平,向CPU提出请求。

4 如果CPU内部的标志寄存器中的IF(中断允许标志)为0,CPU不响应该请求。若IF=1,CPU在执行完当前指令后,从CPU的INTA引脚上向8259发出两个负脉冲。

5第一个 INTA负脉冲到达8259时,8259完成以下三项工作:

a使IRR(中断请求寄存器)的锁存功能失效。这样一来,在IR7~IR0上的请求信号就不会被8259接收。直到第二个INTA负脉冲到达8259时,才又使IRR的锁存功能有效。

b使ISR(服务中寄存器)中的相应位置1。

c使IRR中的相应位清0。

6第二个INTA负脉冲到达8259时,8259完成以下工作:

a将中断类型码(ICW2中的值)送到数据总线上,CPU将其保存在“内部暂存器”中。

b如果ICW4(它设定级中断联方式之特定完全嵌套方式,将在8259的工作方式中详述ICW4)中设置了中断自动结束方式,则将ISR的相应位清0。

RET是普通子程序的返回指令,RET是普通子程序的最后1条指令,它使程序从子程序转到调用该子程序指令LCALL/ACALL的下1条指令执行。

RETI是中断服务子程序的返回指令,RETI是中断服务子程序的最后1条指令,它使程序从中断服务子程序转到中断点继续运行。

RETI指令除了执行返回功能外,还清除内部相应的中断状态寄存器(该状态寄存器由CPU响应中断时置位,宣告CPU当前正在执行中断服务程序),因此中断服务子程序必须用RETI结束,

CPU执行RETI指令后,必须至少再执行一条其它指令才能响应新的中断。

以上就是关于汇编语言中中断INT和子程序调用CALL保护现场时分别压入堆栈的是什么全部的内容,包括:汇编语言中中断INT和子程序调用CALL保护现场时分别压入堆栈的是什么、汇编利用定时中断使静态数码管显示0-9数字、中断服务子程序与普通子程序有哪些异同之处等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/10169376.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-06
下一篇 2023-05-06

发表评论

登录后才能评论

评论列表(0条)

保存