6.7.4. アセンブリ言語を使用した割り込みハンドラのサンプル

割り込みハンドラは、できるだけ速く実行できるようにアセンブリ言語で記述するのが一般的です。以下のセクションではいくつかのサンプルを紹介します。

シングルチャネル DMA 転送

Example 6.16 は、メモリ転送を行うために割り込み駆動型 I/O (ソフト DMA)を実行する割り込みハンドラを示しています。このコードは FIQ ハンドラです。バンク付き FIQ レジスタを使用して、割り込みの間、状態が保持されます。このコードは、0x1C に配置するのが最も適切です。

このサンプルコードでは、以下のようになります。

r8

データが読み出される I/O デバイスのベースアドレスを指します。

IOData

ベースアドレスから、データが読み出される 32 ビットのレジスタまでのオフセットです。このレジスタの読み出しによって、割り込みがクリアされます。

r9

データ転送先のメモリ位置を指します。

r10

転送先の最後のアドレスを指します。

通常の転送を処理する全体のシーケンスには 4 つの命令が使用されます。条件付きの復帰後に配置されているコードは、転送が完了したことを通知するために使用されます。

Example 6.16. FIQ ハンドラ

    LDR     r11, [r8, #IOData]     ; Load port data from the IO device.
    STR     r11, [r9], #4          ; Store it to memory: update the pointer.
    CMP     r9, r10                ; Reached the end ?
    SUBLSS  pc, lr, #4             ; No, so return.
                                   ; Insert transfer complete
                                   ; code here.

ロード命令をバイトロード命令に置き換えることにより、バイト転送を実行できます。メモリから I/O デバイスへの転送は、ロード命令とストア命令の間でアドレシングモードをスワップすることによって実行されます。

デュアルチャネル DMA 転送

Example 6.17Example 6.16 と似ていますが、ここでは 2 つのチャネルが処理されます。このコードは FIQ ハンドラです。バンク付き FIQ レジスタを使用して、割り込みの間、状態が保持されます。このコードは、0x1c に配置するのが最も適切です。

このサンプルコードでは、以下のようになります。

r8

データが読み出される I/O デバイスのベースアドレスを指します。

IOStat

ベースアドレスから、2 つのポートのどちらが割り込みを発生させたかを示すレジスタへのオフセットです。

IOPort1Active

第 1 ポートが割り込みを発生させたことを示すビットマスクです(これ以外の場合は、第 2 ポートが割り込みを発生させたとみなされます)。

IOPort1、IOPort2

読み出される 2 つのデータレジスタへのオフセットです。データレジスタの読み出しによって、対応するポートの割り込みがクリアされます。

r9

第 1 ポートからのデータを転送する先のメモリ位置を指します。

r10

第 2 ポートからのデータを転送する先のメモリ位置を指します。

r11、r12

転送先の最後のアドレスを指します(第 1 ポートには r11、第 2 ポートには r12 が使用されます)。

通常の転送を処理する全体のシーケンスには 9 つの命令が使用されます。条件付きの復帰後に配置されているコードは、転送が完了したことを通知するために使用されます。

Example 6.17. FIQ ハンドラ

    LDR     r13, [r8, #IOStat]      ; Load status register to find which port
                                    ; caused the interrupt.
    TST     r13, #IOPort1Active
    LDREQ   r13, [r8, #IOPort1]     ; Load port 1 data.
    LDRNE   r13, [r8, #IOPort2]     ; Load port 2 data.
    STREQ   r13, [r9], #4           ; Store to buffer 1.
    STRNE   r13, [r10], #4          ; Store to buffer 2.
    CMP     r9, r11                 ; Reached the end?
    CMPLE   r10, r12                ; On either channel?
    SUBNES  pc, lr, #4              ; Return
                            ; Insert transfer complete code here.

ロード命令をバイトロード命令に置き換えることにより、バイト転送を実行できます。メモリから I/O デバイスへの転送は、条件付きロード命令と条件付きストア命令の間でアドレシングモードをスワップすることによって実行されます。

割り込みの優先度

Example 6.18 では、最高 32 個の割り込みソースが適切なハンドラルーチンに送信されます。このサンプルは通常の割り込みベクタ(IRQ)に使用される目的で設計されているため、0x18 から分岐させる必要があります。

外部ハードウェアは、割り込みの優先度を決定し、I/O レジスタに優先度の高いアクティブな割り込みを割り当てるために使用します。

このサンプルコードでは、以下のようになります。

IntBase

割り込みコントローラのベースアドレスを保持します。

IntLevel

最高優先度のアクティブな割り込みを含むレジスタのオフセットを保持します。

r13

小さな Full Descending スタックを指します。

割り込みは、このコードへの分岐も含め、10 命令実行後にイネーブルされます。

各割り込みに固有のハンドラは、さらに 2 命令実行後に(すべてのレジスタがスタックに保存された状態で)開始されます。

また、各ハンドラの最後の 3 命令は割り込みをもう一度オフにした状態で実行されるため、SPSR は安全にスタックから復元できます。

Note

Application Note 30 - Software Prioritization of Interruptsでは、上記のハードウェアを使用する方法ではなく、ソフトウェアを使用して複数ソースの割り込みの優先度を決定する方法について説明しています。

Example 6.18. 

    ; first save the critical state
    SUB     lr, lr, #4              ; Adjust the return address
                                    ; before we save it.
    STMFD   sp!, {lr}               ; Stack return address
    MRS     r14, SPSR               ; get the SPSR ...
    STMFD   sp!, {r12, r14}         ; ... and stack that plus a
                                    ; working register too.
                                    ; Now get the priority level of the
                                    ; highest priority active interrupt.
    MOV     r12, #IntBase           ; Get the interrupt controller's
                                    ; base address.
    LDR     r12, [r12, #IntLevel]   ; Get the interrupt level (0 to 31).

    ; Now read-modify-write the CPSR to enable interrupts.

    MRS     r14, CPSR               ; Read the status register.
    BIC     r14, r14, #0x80         ; Clear the I bit
                                    ; (use 0x40 for the F bit).
    MSR     CPSR_c, r14             ; Write it back to re-enable
                                    ; interrupts and
    LDR     pc, [pc, r12, LSL #2]   ; jump to the correct handler.
                                    ; PC base address points to this
                                    ; instruction + 8
    NOP                             ; pad so the PC indexes this table.

                                    ; Table of handler start addresses
    DCD     Priority0Handler
    DCD     Priority1Handler
    DCD     Priority2Handler
; ...
    Priority0Handler
    STMFD   sp!, {r0 - r11}         ; Save other working registers.
                                    ; Insert handler code here.
; ...
    LDMFD   sp!, {r0 - r11}         ; Restore working registers (not r12).

    ; Now read-modify-write the CPSR to disable interrupts.
    MRS     r12, CPSR               ; Read the status register.
    ORR     r12, r12, #0x80         ; Set the I bit
                                    ; (use 0x40 for the F bit).
    MSR     CPSR_c, r12             ; Write it back to disable interrupts.

    ; Now that interrupt disabled, can safely restore SPSR then return.
    LDMFD   sp!, {r12, r14}         ; Restore r12 and get SPSR.
    MSR     SPSR_csxf, r14          ; Restore status register from r14.
    LDMFD   sp!, {pc}^              ; Return from handler.
Priority1Handler
; ...

コンテキストスイッチ

Example 6.19 では、ユーザモードのプロセスへのコンテキストスイッチが実行されます。このコードでは、実行準備が整ったプロセッサのプロセス制御ブロック(PCB)を指すポインタのリストが使用されています。

Figure 6.5 は、このサンプルで使用されている PCB のレイアウトを示しています。

Figure 6.5. PCB のレイアウト

PCB のレイアウト

r12 は次に実行されるプロセスの PCB を指すポインタを指します。このリストの終わりにはゼロポインタが配置されます。レジスタ r13 は PCB へのポインタであり、タイムスライスの際には保存されるため、エントリ時には実行中のプロセスの PCB を指します。

Example 6.19. 

    STMIA   r13, {r0 - r14}^        ; Dump user registers above r13.
    MRS     r0, SPSR                ; Pick up the user status
    STMDB   r13, {r0, lr}           ; and dump with return address below.
    LDR     r13, [r12], #4          ; Load next process info pointer.
    CMP     r13, #0                 ; If it is zero, it is invalid
    LDMNEDB r13, {r0, lr}           ; Pick up status and return address.
    MSRNE   SPSR_cxsf, r0           ; Restore the status.
    LDMNEIA r13, {r0 - r14}^        ; Get the rest of the registers
    NOP
    SUBNES  pc, lr, #4              ; and return and restore CPSR.
                    ; Insert "no next process code" here.
Copyright © 2002-2006 ARM Limited. All rights reserved.ARM DUI 0203GJ
Non-Confidential