在上篇《GNU ARM汇编–(四)中止汇编之非嵌套中止处理》中剖析了最简略的中止处理的写法,再看TQ2440发动代码中的中止向量表的写法就一望而知了.今日抽时间对嵌套中止处理的学习做下收拾.
嵌套中止处理的中心代码如下:
;/*; * ____________________________________________________________________; * ; * Copyright (c) 2004, Andrew N. Sloss, Chris Wright and Dominic Symes; * All rights reserved.; * ____________________________________________________________________; * ; * NON-COMMERCIAL USE License; * ; * Redistribution and use in source and binary forms, with or without ; * modification, are permitted provided that the following conditions ; * are met: ; * ; * 1. For NON-COMMERCIAL USE only.; * ; * 2. Redistributions of source code must retain the above copyright ; * notice, this list of conditions and the following disclaimer. ; * ; * 3. Redistributions in binary form must reproduce the above ; * copyright notice, this list of conditions and the following ; * disclaimer in the documentation and/or other materials provided ; * with the distribution. ; * ; * 4. All advertising materials mentioning features or use of this ; * software must display the following acknowledgement:; * ; * This product includes software developed by Andrew N. Sloss,; * Chris Wright and Dominic Symes. ; * ; * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS AND ANY ; * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ; * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE ; * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, ; * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ; * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ; * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ; * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ; * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ; * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY ; * OF SUCH DAMAGE. ; * ; * If you have questions about this license or would like a different; * license please email :; * ; * andrew@sloss.net; * ; * ; */;/*; *; * Module : nih9_9.s; * Descriptions : Nested Interrupt Handler; * Example : 9.9; * OS : generic; * Platform : generic; * History :; *; * 31th December 2003; * - added header; *; */EXPORT nestedInterruptHandlerMaskmd EQU 0x1f ; processor mode maskSVC32md EQU 0x13 ; SVC modeI_Bit EQU 0x80 ; IRQ bit FRAME_R0 EQU 0x00 FRAME_R1 EQU FRAME_R0+4FRAME_R2 EQU FRAME_R1+4FRAME_R3 EQU FRAME_R2+4FRAME_R4 EQU FRAME_R3+4FRAME_R5 EQU FRAME_R4+4FRAME_R6 EQU FRAME_R5+4FRAME_R7 EQU FRAME_R6+4FRAME_R8 EQU FRAME_R7+4FRAME_R9 EQU FRAME_R8+4FRAME_R10 EQU FRAME_R9+4FRAME_R11 EQU FRAME_R10+4FRAME_R12 EQU FRAME_R11+4FRAME_PSR EQU FRAME_R12+4FRAME_LR EQU FRAME_PSR+4FRAME_PC EQU FRAME_LR+4FRAME_SIZE EQU FRAME_PC+4AREA nih9_9,CODE,readonlynestedInterruptHandler ; instruction state : comment SUB r14,r14,#4 ; 2 :STMDB r13!,{r0-r3,r12,r14} ; 2 : save context;BL read_RescheduleFlag ; 3 : more processingCMP r0,#0 ; 3 : if processing?LDMNEIA r13!,{r0-r3,r12,pc}^ ; 4 : then return MRS r2,SPSR ; 5 : copy SPSR_irqMOV r0,r13 ; 5 : copy r13_irqADD r13,r13,#6*4 ; 5 : reset stackMRS r1,CPSR ; 6 : copy CPSRBIC r1,r1,#Maskmd ; 6 :ORR r1,r1,#SVC32md ; 6 :MSR CPSR_c,r1 ; 6 : change SVC modeSUB r13,r13,#FRAME_SIZE-FRAME_R4 ; 7 : make stack space STMIA r13,{r4-r11} ; 7 : save r4-r11LDMIA r0,{r4-r9} ; 7 : r4-r9 IRQ stack BIC r1,r1,#I_Bit ; 8 : MSR CPSR_c,r1 ; 8 : enable intSTMDB r13!,{r4-r7} ; 9 : save r4-r7 SVCSTR r2,[r13,#FRAME_PSR] ; 9 : save PSRSTR r8,[r13,#FRAME_R12] ; 9 : save r12STR r9,[r13,#FRAME_PC] ; 9 : save pc STR r14,[r13,#FRAME_LR] ; 9 : save lr; LDMIA r13!,{r0-r12,r14} ; 11 : restore contextMSR SPSR_cxsf,r14 ; 11 : restore SPSRLDMIA r13!,{r14,pc}^ ; 11 : returnread_RescheduleFlag; MOV r0,#0 ; more processing MOV pc,r14 ; returnEND
代码的要害便是在中止后切换到SVC形式下,使用svc mode的stack来完结中止嵌套进程的备份以及康复操作.从代码中能够看到,从R0到PC都在栈中有备份,这儿咱们叫栈帧.记住《深化了解计算机体系》一书在讲x86汇编的函数调用时也是栈帧的概念.这点上中止嵌套和函数调用有相似之处.有了这个栈帧,使用压栈出栈操作就全部ok了.
刚看这个代码,对有个当地有疑问,便是觉得中止开早了:
BIC r1,r1,#I_Bit ; 8 :
MSR CPSR_c,r1 ; 8 : enable int
STMDB r13!,{r4-r7} ; 9 : save r4-r7 SVC
STR r2,[r13,#FRAME_PSR] ; 9 : save PSR
STR r8,[r13,#FRAME_R12] ; 9 : save r12
STR r9,[r13,#FRAME_PC] ; 9 : save pc
STR r14,[r13,#FRAME_LR] ; 9 : save lr
觉得开中止的代码应该放在后边,这样才干确保svc mode下的stack frame不会被损坏.但在草稿纸上画一下irq和svc下的stack图,就发现仓库操作并没有问题.能够假定刚开中止立马就有新的中止了,r4-r7 r8 r9都有在STMIA r13,{r4-r11} 中保存到svc的stack中,LDMIA r0,{r4-r9} 和STMDB r13!,{r4-r7} 确保了开始的r0-r3在栈中,而LDMIA r0,{r4-r9}和STR r8,[r13,#FRAME_R12] 以及STR r9,[r13,#FRAME_PC] 确保了R12和PC,确保正确回来.(这儿的r9装的是r14_irq,所以pc便是r14_irq,这样就确保了从中止服务例程中回来).至于STR r14,[r13,#FRAME_LR]中的r14是r14_svc,将其压入svc的stack中,中止例程用bl就不会呈现错误了,在最终LDMIA r13!,{r14,pc}^ 中r14得到康复.而r2保存的是spsr,也便是svc形式的状况,一向不变,不必忧虑会被掩盖.
最终,再看了一遍图,觉得r10和r11的帧能够省去,由于r4-r9是用来存atpcs的r0-r3,r12,r14,而r10和r11用不到.圆满能够省点空间和时间,详细的待会试验一下.
下面给出实践的嵌套中止处理,使用r10来保存INTOFFSET的值,依据该值来断定是什么中止,然后做不同的处理.详细的作用是:代码会做流水灯的动作,Key1代表INT1,中止处理动作是4个灯全全亮然后全灭,Key4代表代表INT0,中止处理动作是第一个灯和第三个灯亮,然后第二个灯和第四个灯亮.
/*simple interruptioncopyleft@dndxhej@gmail.com*/.equ Maskmd, 0x1f @ processor mode mask.equ SVC32md, 0x13 @ SVC mode.equ I_Bit, 0x80 @ IRQ bit .equ FRAME_R0, 0x00 .equ FRAME_R1, FRAME_R0+4.equ FRAME_R2, FRAME_R1+4.equ FRAME_R3, FRAME_R2+4.equ FRAME_R4, FRAME_R3+4.equ FRAME_R5, FRAME_R4+4.equ FRAME_R6, FRAME_R5+4.equ FRAME_R7, FRAME_R6+4.equ FRAME_R8, FRAME_R7+4.equ FRAME_R9, FRAME_R8+4.equ FRAME_R10, FRAME_R9+4.equ FRAME_R11, FRAME_R10+4.equ FRAME_R12, FRAME_R11+4.equ FRAME_PSR, FRAME_R12+4.equ FRAME_LR, FRAME_PSR+4.equ FRAME_PC, FRAME_LR+4.equ FRAME_SIZE, FRAME_PC+4.equ NOINT, 0xc0.equ WTCON, 0x53000000.equ GPBCON, 0x56000010 @led.equ GPBDAT, 0x56000014 @led.equ GPBUP, 0x56000018 @led.equ GPFCON, 0x56000050 @interrupt config.equ EINTMASK, 0x560000a4.equ EXTINT0, 0x56000088.equ EXTINT1, 0x5600008c.equ EXTINT2, 0x56000090.equ INTMSK, 0x4A000008.equ EINTPEND, 0x560000a8.equ INTSUBMSK, 0X4A00001C.equ SRCPND, 0X4A000000.equ INTPND, 0X4A000010.equ INTOFFSET, 0x4A000014.global _start_start: b resetldr pc, _undefined_instructionldr pc, _software_interruptldr pc, _prefetch_abortldr pc, _data_abortldr pc, _not_used@b irqldr pc, _irqldr pc, _fiq_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadbeefreset:ldr r3, =WTCONmov r4, #0x0 str r4, [r3] @ disable watchdog ldr r0, =GPBCONldr r1, =0x15400str r1, [r0]ldr r2, =GPBDATldr r1, =0x160str r1, [r2]bl delaymsr cpsr_c, #0xd2 @进入中止形式ldr sp, =0xc00 @中止形式的栈指针界说msr cpsr_c, #0xd3 @进入svc形式ldr sp, =0xfff @设置svc形式的栈指针@--------------------------------------------ldr r0, =GPBUPldr r1, =0x03f0 str r1, [r0] ldr r0, =GPFCONldr r1, =0x2ea@0x2 str r1, [r0] ldr r0, =EXTINT0@ldr r1, =0x8f888@0x0@0x8f888 @~(7|(7<<4)|(7<<8)|(7<<16))ldr r1, =0xafaaastr r1, [r0] ldr r0, =EINTPENDldr r1, =0xf0@0b10000str r1, [r0] ldr r0, =EINTMASKldr r1, =0x00@0b00000str r1, [r0] ldr r0, =SRCPNDldr r1, =0xff@0x1@0b11111str r1, [r0] ldr r0, =INTPNDldr r1, =0xff@0x1@0b11111str r1, [r0] ldr r0, =INTMSKldr r1, =0xffffff00@0b00000str r1, [r0] MRS r1, cpsrBIC r1, r1, #0x80MSR cpsr_c, r1bl mainirq:sub r14,r14,#4 @ 2 :stmdb sp!,{r0-r3,r12,r14} @ 2 : save context@@BL read_RescheduleFlag @ 3 : more processing@CMP r0,#0 @ 3 : if processing?@LDMNEIA sp!,{r0-r3,r12,pc}^ @ 4 : then return @@@@@@@@@@@@@@@@ldr r10,=INTOFFSET @用r10保存中止的offsetldr r10,[r10]ldr r0,=EINTPENDldr r1,=0xf0str r1,[r0] ldr r0, =SRCPNDldr r1, =0x3f@0b11111str r1, [r0] ldr r0, =INTPNDldr r1, =0x3f@0b11111str r1, [r0] @@@@@@@@@@@@@@mrs r2,SPSR @ 5 : copy SPSR_irqmov r0,sp @ 5 : copy sp_irqadd sp,sp,#6*4 @ 5 : reset stackmrs r1,CPSR @ 6 : copy CPSRbic r1,r1,#Maskmd @ 6 :orr r1,r1,#SVC32md @ 6 :msr CPSR_c,r1 @ 6 : change SVC modesub sp,sp,#FRAME_SIZE-FRAME_R4 @ 7 : make stack space stmia sp,{r4-r11} @ 7 : save r4-r11ldmia r0,{r4-r9} @ 7 : r4-r9 IRQ stack bic r1,r1,#I_Bit @ 8 : msr CPSR_c,r1 @ 8 : enable intstmdb sp!,{r4-r7} @ 9 : save r4-r7 SVCstr r2,[sp,#FRAME_PSR] @ 9 : save PSRstr r8,[sp,#FRAME_R12] @ 9 : save r12str r9,[sp,#FRAME_PC] @ 9 : save pc str r14,[sp,#FRAME_LR] @ 9 : save lr@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@cmp r10,#0x0bleq blink1cmp r10,#0x1bleq blink3@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@LDMIA sp!,{r0-r12,r14} @ 11 : restore contextMSR SPSR_cxsf,r14 @ 11 : restore SPSRLDMIA sp!,{r14,pc}^ @ 11 : returndelay:ldr r3,=0xffffdelay1:sub r3,r3,#1cmp r3,#0x0bne delay1mov pc,lrblink1:ldr r0, =GPBDATldr r1, =0x000str r1, [r0]ldr r3,=0xffffdelay2:sub r3,r3,#1cmp r3,#0x0bne delay2ldr r0, =GPBDATldr r1, =0x1f0str r1, [r0]ldr r3,=0xffffdelay3:sub r3,r3,#1cmp r3,#0x0bne delay3mov pc,lrblink2:ldr r0, =GPBDATldr r1, =0x140str r1, [r0]ldr r3,=0xffffdelay12:sub r3,r3,#1cmp r3,#0x0bne delay12ldr r0, =GPBDATldr r1, =0xa0str r1, [r0]ldr r3,=0xffffdelay13:sub r3,r3,#1cmp r3,#0x0bne delay13mov pc,lrblink3:ldr r0, =GPBDATldr r1, =0x0a0str r1, [r0]stmfd sp!,{lr}bl delayldr r0, =GPBDATldr r1, =0x140str r1, [r0]bl delayldmfd sp!,{lr}mov pc,lrmain:ledloop:ldr r1,=0x1c0str r1,[r2]bl delayldr r1,=0x1a0str r1,[r2]bl delayldr r1,=0x160str r1,[r2]bl delayldr r1,=0x0e0str r1,[r2]bl delayb ledloopundefined_instruction:nopsoftware_interrupt:nopprefetch_abort: nopdata_abort:nopnot_used:nopfiq:nop
代码比较繁琐,有几点值得留意:在嵌套中止处理中,压栈后先保存INTOFFSET的值,再清中止(SRCPND和INTPND).由于SRCPND和INTPND铲除后INTOFFSET就主动铲除了,所以要先保存.在中止服务程序中,是能够用bl跳转到各自的中止服务程序的,比方blne blink1和blne blink3,值得比照的blink1和blink3,他们的不同在于blink1自己用代码做了延时,而blink3是调用bl delay做的延时,那么这个时分要留意的便是lr的push和pop操作,否则lr就被掩盖了,程序不能正确回来了.
留意了以上两点,程序上达到了嵌套处理的作用.由于选用的是下降边缘触发,而按键没有防抖处理,有时分单按一个键就有嵌套中止了.最终总结一下这种处理的优缺陷:长处是在为一个中止处理服务完结前答应其它中止,以缩短中止推迟;而缺陷是不处理中止的优先级,因而低优先级的中止会堵塞高优先级的中止.