;
;  TOPPERS/JSP Kernel
;      Toyohashi Open Platform for Embedded Real-Time Systems/
;      Just Standard Profile Kernel
;
;  Copyright (C) 2000-2004 by Embedded and Real-Time Systems Laboratory
;                              Toyohashi Univ. of Technology, JAPAN
;  Copyright (C) 2001-2007 by Industrial Technology Institute,
;                              Miyagi Prefectural Government, JAPAN
;  Copyright (C) 2001-2004 by Dep. of Computer Science and Engineering
;                   Tomakomai National College of Technology, JAPAN
;
;  上記著作権者は，以下の (1)〜(4) の条件か，Free Software Foundation
;  によって公表されている GNU General Public License の Version 2 に記
;  述されている条件を満たす場合に限り，本ソフトウェア（本ソフトウェア
;  を改変したものを含む．以下同じ）を使用・複製・改変・再配布（以下，
;  利用と呼ぶ）することを無償で許諾する．
;  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
;      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
;      スコード中に含まれていること．
;  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
;      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
;      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
;      の無保証規定を掲載すること．
;  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
;      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
;      と．
;    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
;        作権表示，この利用条件および下記の無保証規定を掲載すること．
;    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
;        報告すること．
;  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
;      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
;
;  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
;  よびTOPPERSプロジェクトは，本ソフトウェアに関して，その適用可能性も
;  含めて，いかなる保証も行わない．また，本ソフトウェアの利用により直
;  接的または間接的に生じたいかなる損害に関しても，その責任を負わない．
;
;  @(#) $Id: cpu_support.src,v 1.8 2007/03/23 07:58:33 honda Exp $
;

;       プロセッサ依存モジュール アセンブリ言語部（H8用）
;　　　　　カーネル内部で使用する定義

        .CPU 300HA
        .SECTION P, CODE, ALIGN=2

        .INCLUDE "sys_config_asm.inc"
        .INCLUDE "cpu_config_asm.inc"
        .INCLUDE "offset.inc"

;　　共通部とのインターフェース
;　　　変数
        .IMPORT _runtsk
        .IMPORT _schedtsk
        .IMPORT _reqflg
        .IMPORT _enadsp
;　　　関数
        .IMPORT _ext_tsk
        .IMPORT _call_texrtn

;　　H8依存部独自
;　　　変数
        .IMPORT _intnest
        .IMPORT _iscpulocked
        .IMPORT _task_intmask
;　　　関数
        .IMPORT _cpu_experr

;
;  NMIを除くすべての割込みを禁止
;
DISINT: .DEFINE         "orc.b  #CCR_DISINT_ALL, ccr"

;
;  すべての割込みを許可
;
ENAINT: .DEFINE         "andc.b #CCR_ENAINT_ALL, ccr"


;
;  タスクディスパッチャ
;
;    　_dispatch は、割込み外ネストカウンタ = 0,割込み禁止状態
;    で呼び出さなければならない．_exit_and_dispatch も，割込みネスト
;    カウンタ = 0・割込み禁止状態で呼び出すのが原則であるが，カーネル
;    起動時に対応するため，割込みネストカウンタ = 1で呼び出した場合に
;    も対応している．

        .GLOBAL _dispatch
_dispatch:
        push.l  er2                     ; er2〜6を保存
        push.l  er3
        push.l  er4
        push.l  er5
        push.l  er6
        mov.l   @_runtsk, er0           ; er0 <- runtsk
        mov.l   sp, @(TCB_sp, er0)      ; タスクスタックポインタを保存
        mov.l   #dispatch_r, er1        ; 実行再開番地を保存
        mov.l   er1, @(TCB_pc, er0)
        bra     dispatcher


; タスクスイッチの結果ここから実行が再開される

dispatch_r:
        pop.l   er6                     ; er2〜6を復元
        pop.l   er5
        pop.l   er4
        pop.l   er3
        pop.l   er2
                                ; タスク例外許可状態のチェック
        mov.l   @_runtsk, er0           ; er0←runtsk
        mov.b   @(TCB_enatex,er0),r1l
        btst   #TCB_enatex_bit,r1l      ; TCB.enatex=0->CCR.Z=1
        beq    _dispatch_r_1    ;　タスク例外処理禁止状態ならジャンプ

                                ; タスク例外処理要求のチェック
        mov.w   @(TCB_texptn,er0),r1
        beq     _dispatch_r_1   ; 保留例外要因がなければジャンプ

        jmp     @_call_texrtn   ; タスク例外処理処理ルーチンの呼び出し
                                ;　　_call_texrtnからここには戻らず
                                ;　　dispatch()の呼び出し元へ戻る
_dispatch_r_1:
        rts     ;　dispatch()の呼び出し元へリターン



        .GLOBAL _exit_and_dispatch
_exit_and_dispatch:
        ; 割り込み禁止で呼ばれる。
        mov.b   #0, r0l         ; 割込みネストカウンタをクリア
        mov.b   r0l, @_intnest

;
;  ディスパッチャ本体
;
dispatcher:
        ; ここには割り込み禁止で来ること

        ; ここでruntskにschedtskを代入するのは２つの意味がある。
        ; 　(1) schedtsk != NULLの場合
        ; 　　　　通常のタスク切り替えを行う。
        ; 　(2) schedtsk == NULLの場合
        ; 　　　　runtskにNULLを代入しておく。
        ; 　　　　（dispatcher_1以降の割込み待ちで割込みが入り、その中で
        ; 　　　　　iget_tid()がコールされたときに正しくTSK_NONEを返すため
        ; 　　　　　には、実行状態のタスクがない時に、runtskをNULLにして
        ; 　　　　　いる必要がある。）

        mov.l   @_schedtsk, er0         ; er0 <- schedtsk
        mov.l   er0, @_runtsk
        beq     dispatcher_1            ; schedtskが無ければジャンプ
        mov.l   @(TCB_sp, er0), sp
        mov.l   @(TCB_pc, er0), er0     ; 実行再開番地を復帰
        jmp     @er0

        ;
        ;　実行すべきタスクがない場合の割込み待ち
        ;
        ;  ここで割込みモードに切り換えるのは，ここで発生する割込み処理
        ;  にどのスタックを使うかという問題の解決と，割込みハンドラ内で
        ;  のタスクディスパッチの防止という二つの意味がある．
dispatcher_1:

        mov.l   #STACKTOP, sp   ; スタックを割り込みスタックに切替え
        mov.b   #1, r0l         ;  割込みネストカウンタを１にする
        mov.b   r0l, @_intnest

        ;　r1l：割込み許可時にccrに設定する値
        ;　　　　割込みハンドラ内ではchg_ipmが使用できないので、
        ;　　　　task_intmaskは1回読み込んでおけば、OK
 .AIFDEF SUPPORT_CHG_IPM
        mov.b     @_task_intmask, r1l
 .AELSE ; SUPPORT_CHG_IPM
        mov.b     #CCR_ENAINT_ALL, r1l
 .AENDI ; SUPPORT_CHG_IPM

 .AIFDEF EXCLUSIVE_BUS
        ;　下記の説明を参照
        mov.l   H8BRCR, er2
 .AENDI ; EXCLUSIVE_BUS

        ;  CPUロックフラグをクリア
        mov.w   #0, r3
        mov.w   r3, @_iscpulocked

dispatcher_2_enable_interrupt:
 .AIFDEF EXCLUSIVE_BUS
        ;　sleepしている間にバス権要求が競合しないようにバス権の
        ;　外部への解放を禁止する。
        ;　ハードウェア・マニュアル「6.4.4 ソフトウェア・スタンバイ・
        ;　モードへの遷移」参照
        bclr    H8BRCR_BRLE_BIT, @er2
 .AENDI ; EXCLUSIVE_BUS

        ;　ldc命令直後は割込み禁止であり、割込み要求があっても
        ;　次の命令が実行される。
        ;　（割込みから戻ってきてsleepするケースはない）
        ldc.b     r1l, ccr              ; 割り込み許可
        sleep                           ; 割込み待ち
        DISINT                          ; 割り込み禁止

 .AIFDEF EXCLUSIVE_BUS
        bset    H8BRCR_BRLE_BIT, @er2
 .AENDI ; EXCLUSIVE_BUS

        mov.w   @_reqflg, r0            ; reqflgのチェック
                                        ;  reqflgはBOOL型 -> int型(16bits)
        beq     dispatcher_2_enable_interrupt
        mov.w   #0,r0
        mov.w   r0, @_reqflg            ; reqflgのクリア
        mov.b   r0l, @_intnest          ; 割込みネストカウンタをクリア
        bra     dispatcher


;
;  割込みハンドラ入口処理
;　　割込み要因に依らず、共通な部分
;　　（vector.srcのINTHDR_ENTRYからの続き）
;
;　　　　以下の状態でここにやってくる
;　　　　　・割込み禁止
;　　　　　・er0〜er3退避済み
;　　　　　・er2：C言語ルーチンの先頭アドレス
;　　　　　・r3l：割込み許可時に割込みマスクに設定する値

        .GLOBAL _common_interrupt_process
_common_interrupt_process:
        mov.b   @_intnest, r0l          ;　割込みネストカウンタのチェック
        bne     _interrupt_from_int     ;　多重割込みの処理へ

        ;
        ;　初段の割込みの場合
        ;
        mov.b   #1, r1l                 ;　割込みネストカウンタ←１
        mov.b   r1l, @_intnest
        mov.l   sp, er0
        mov.l   #STACKTOP, sp           ;　スタック切替
        push.l  er0                     ;　タスクスタックポインタの退避
        ldc.b   r3l, ccr                ;　割込み許可
        jsr     @er2                    ;　C言語ルーチン呼び出し
        DISINT                          ;　割込み禁止
        mov.b   #0, r0l                 ;　割込みネストカウンタのクリア
        mov.b   r0l, @_intnest
        mov.l   @sp, sp                 ;　スタック切替
        mov.w   @_reqflg, e0            ;　reqflgのチェック
        beq     _ret_to_task_int        ;　reqflgがFALSEならret_to_task_intに飛ぶ
        mov.w   #0, e0
        mov.w   e0, @_reqflg            ;　reqflgをクリア
        bra     ret_int                 ;　出口処理へジャンプ

        ;
        ;　多重割込みの場合;
        ;　　割込み禁止でここにやってくる
        ;
        ;　　割込みネストカウンタのインクリメントは以下の理由で
        ;　　省略している
        ;　　　・割込みが2レベルしかない
        ;　　　・CPU例外をサポートしない
        ;　　　　　exc_sense_context()のことを考慮しなくても良い
        ;
_interrupt_from_int:
        ;　割込みネストカウンタのインクリメントは省略
        ldc.b   r3l, ccr                ;　割込み許可
        jsr     @er2                    ;　C言語ルーチン呼び出し
        DISINT                          ;　割込み禁止
        ;　割込みネストカウンタのディクリメントは省略

        ;　本来はここで戻り先が、割込み待ちのsleep命令かどうかのチェッ
        ;　クが必要である。（もし、割込みハンドラ内でreqflg=TRUEにして
        ;　もディスパッチャが呼ばれない＝タスクが実行されない）
        ;　H8の場合、sleep命令の直前にある割込み許可のldc命令の実行直
        ;　後は割込み禁止であり、割込み要求があったとしても割込みは受
        ;　け付けないで、sleep命令が実行される。そのため、割込みから
        ;　戻った直後にsleep命令が実行されるケースは考慮しなくて良い。

        ;
        ;　ディスパッチャを呼ばずに、割込み元に戻る場合
        ;
_ret_to_task_int:
        pop.l   er3                     ;　レジスタを復元
        pop.l   er2
        pop.l   er1
        pop.l   er0
        rte                             ;　割込み元へリターン


; 出口処理においてスタックポインタが指している番地からCCRレジスタが
; 積まれている番地までのオフセットの定義
;
;　 +0:er3
;　 +4:er2
;　 +8:er1
;　+12:er0
;  +16:ccr ←ここをsp相対アクセスする

OFFSET_CCR:             .DEFINE         "16"

;
;  割込みハンドラ出口処理
;
; 戻り先がタスクでreqflgがセットされている場合のみここにくる。
; 割込みネストカウンタ = 0,割込み禁止状態,スクラッチレジスタを
; 保存した状態で呼び出すこと。

ret_int:
                                        ; enadspのチェック
        mov.w   @_enadsp, r0            ;  enadspはBOOL型 -> int型(16bits)
        beq     ret_int_1               ;  ディスパッチ禁止ならret_int_1へ

        mov.l   @_runtsk, er0           ; er0 <- runtsk
        mov.l   @_schedtsk, er1         ; er1 <- schedtsk
        cmp.l   er0, er1                ; runtsk と schedtsk を比較
        beq     ret_int_1               ; 同じならret_int_1へ

        push.l  er4                     ;　レジスタ退避
        push.l  er5
        push.l  er6
        mov.l   sp, @(TCB_sp, er0)      ; タスクスタックポインタを保存
        mov.l   #ret_int_r, er1         ; 実行再開番地を保存
        mov.l   er1, @(TCB_pc, er0)
        bra     dispatcher

        ;
        ;  割込みの出口でディスパッチャからここに戻ってくる
        ;
ret_int_r:
        pop.l   er6                     ;　レジスタ復元
        pop.l   er5
        pop.l   er4

        ;
        ;　タスクスイッチしない場合はここから合流する
        ;
ret_int_1:
                                        ; タスク例外許可状態のチェック
        mov.l   @_runtsk, er0           ; er0←runtsk
        mov.b   @(TCB_enatex,er0),r1l
        btst   #TCB_enatex_bit,r1l      ; TCB.enatex=0->CCR.Z=1
        beq    ret_int_2                ;　タスク例外禁止状態ならジャンプ

                                        ; タスク例外処理要求のチェック
        mov.w   @(TCB_texptn,er0),r1
        beq     ret_int_2               ; 保留例外要因がなければジャンプ
        jsr     @_call_texrtn           ; タスク例外処理ルーチン起動

ret_int_2:
        ;  CPUロックフラグをクリア
        mov.w   #0, r0
        mov.w   r0, @_iscpulocked

 .AIFDEF SUPPORT_CHG_IPM
        ;  スタックに積んであるccr（戻り先の割込みマスク）を
        ;　task_intmaskにする。（下記参照）
        mov.b   @(OFFSET_CCR, sp), r0l
        mov.b   @_task_intmask, r1l
        and.b   #CCR_ENAINT_ALL, r0l
        or.b    r1l, r0l
        mov.b   r0l, @(OFFSET_CCR, sp)
 .AENDI ;SUPPORT_CHG_IPM
                                        ; レジスタを復元
        pop.l   er3                     ;　 +0:er3
        pop.l   er2                     ;　 +4:er2
        pop.l   er1                     ;　 +8:er1
        pop.l   er0                     ;　+12:er0
                                        ;  +16:ccr ←ここを書き換える
        rte                             ;　割込み元にリターン

;
;  タスク起動処理
;
;　　　　CPUロック状態で呼び出される。
;
;　　　　タスク実行開始直後はタスク例外処理禁止状態になっているので、
;　　　　_activate_rでは保留例外要因のチェックをする必要がない。
;

        .GLOBAL _activate_r
_activate_r:
        ;　t_unlock_cpu 相当の処理
        mov.w   #0, r0               ;  CPUロックフラグをクリア
        mov.w   r0, @_iscpulocked
 .AIFDEF SUPPORT_CHG_IPM
        mov.b   @_task_intmask, r0l  ;  割込みマスクをtask_intmaskに
        ldc.b   r0l, ccr
 .AELSE         ; SUPPORT_CHG_IPM
        ENAINT                       ;  割込み許可
 .AENDI ;SUPPORT_CHG_IPM

        mov.l   #_ext_tsk, er0
        push.l  er0             ;  タスクからの戻り番地を設定

        ;  ここで割込みが発生してタスクスイッチが起こったとしても、
        ;  ここに戻くるときには、runtskの値は元に戻っている。

        mov.l   @_runtsk, er2
        mov.l   @(TCB_tinib, er2), er2
        mov.l   @(TINIB_task, er2), er1 ;  タスク起動番地を設定

                                ;  拡張情報（タスクへの引数）を設定
        mov.l   @(TINIB_exinf, er2), er0
        jmp     @er1

;
;  微少時間待ち
;    void sil_dly_nse_long(UW dlytim)
;
;　　　注意事項：
;　　　　標準ではdlytimはUINT型だが16ビットしかないので、
;　　　　UW型に変更している。
;
        .GLOBAL _sil_dly_nse_long
_sil_dly_nse_long:
        sub.l   #SIL_DLY_TIM1, er0
        ble     sil_dly_nse_2
                ;  dlytim < SIL_DLY_TIM1 ならループ終了
sil_dly_nse_1:
        sub.l   #SIL_DLY_TIM2, er0
        bgt     sil_dly_nse_1
                ;  dlytim > SIL_DLY_TIM2 ならループ続行
sil_dly_nse_2:
        rts


;**************************************************************
;  以下、共通部とのインターフェースには含まれない独自の部分
;**************************************************************

;
;  no_reg_exception()
;  CPU例外として登録されていない例外が発生すると呼び出される。
;  例外が発生した時点のPC,ER0〜7を出力してカーネル
;  を停止する。
        .GLOBAL _no_reg_exception
_no_reg_exception:

                        ;  er0〜6を保存
        push.l  er6     ;　　 +0:er0
        push.l  er5     ;　　 +4:er1
        push.l  er4     ;　　 +8:er2
        push.l  er3     ;　　+12:er3
        push.l  er2     ;　　+16:er4
        push.l  er1     ;　　+20:er5
        push.l  er0     ;　　+24:er6
                        ;　　+25:crr
                        ;　　+26:pc
                        ;　　+28〜:割込み前に使用されていたスタック領域

        mov.l   sp, er0         ;  引数設定
                                ;    SPは+28で、発生時点の値
        jsr     @_cpu_experr    ;  cpu_experr()の呼び出し


        .END

