k-uOS のプログラム・コード

task queue 群

	differed called function
	dynamic polling function queue
	delay queue
	ready queue
	looping wait queue
	(event queue)
delay queue
TCB RAM データ構造
                                 StTaskBase
  ┌─────┬───────┬───────────────────────┐
  │1 bit     │3 bit         │                   12 bit                     │
  │m_blReady │m_enTaskState │                   m_wdDelay                  │
  └─────┴───────┴───────────────────────┘

	enum EnmTaskState { // 
          EnDormant_=0
         // wait() の戻り値は m_enTaskState そのものの値を返す
         ,EnWait_ // Time up と Restart を区別する
         ,EnLoopingWait_
         ,EnTimeup_=3
         ,EnStart_=4
         // Wait or EnLoopingWait_ に対し、リスタートを発行すると EnRestart になる
         ,EnRestart_
         , EnRun_ = 6
         , EnLoopRun_
	};
	
	struct StTaskBase {
	    unsigned m_wdDelay:12;
	    unsigned m_enTaskState:3;
	    unsigned m_blReady:1;        /* MSB */
	};
       StFrameContext ( StTaskBase を継承しているつもり)

     ┌───────────┐
     │StTaskBase            │
     ├───────────┤
     │return address        │
     │    m_pTimeUpAddress  │
     ├───────────┤
     │stack frame Context   │
     │    int m_ppStCnxtt_sp│
     │    int m_inEBX       │
     │    int m_inESI       │
     │    int m_inEDI       │
     └───────────┘


	struct StFrameContext {
	    struct StTaskBase m_stTaskBase;
	    void (*m_pTimeUpAddress)( struct StFrameContext*);
	    // VC task context
	    int m_ppStCnxtt_sp;
	    int m_inEBX;
	    int m_inESI;
	    int m_inEDI;
	};
       StStckFrmContext ( StFrameContext を継承しているつもり)

     ┌───────────┐
     │StTaskBase            │
     ├───────────┤
     │return address        │
     │    m_pTimeUpAddress  │
     ├───────────┤
     │stack frame Context   │
     │    int m_ppStCnxtt_sp│
     │    int m_inEBX       │
     │    int m_inESI       │
     │    int m_inEDI       │
     ├───────────┤
     │task stack pointer    │
     │        m_pByStack    │
     └───────────┘


	struct StStckFrmContext {
	    struct StTaskBase m_stTaskBase;
	    void (*m_pTimeUpAddress)( struct StFrameContext*);
	    // VC task context
	    int m_ppStCnxtt_sp;
	    int m_inEBX;
	    int m_inESI;
	    int m_inEDI;
    	unsigned char*      m_pByStack;
	};
TCB ROM データ構造
RAM の消費を少なくするため、TCB データのうち、const にできるものを一つにしました。

      --------- StGroupVTable --------

┌───────────────────────────┐  
│RAM TCB pointer                                       │TCB RAM pointer
│    StTaskBase* m_pStTaskBase                         │
├───────────────────────────┤
│タスクへのコンテキスト・スイッチ関数 必須             │
│    void (*m_pfPushFrntBk)(StGroupVTable*)            │ StartTask と Delay timeup で使う
├───────────────────────────┤
│    void (*m_pfExecute)(StGroupVTable*)               │ DfBool CntxtCue_ExecuteReadyCueStt(.)
├───────────────────────────┤ の中で呼び出す
│タスク・ループへのコンテキスト・スイッチ関数 選択     │ 上の処理するタスクがないときに 
│    void(*m_pfLooping)( StGroupVTable*);              │executeLoopingCue(.) が呼び出され
├───────────────────────────┤WaitLooping 中のタスクをm_pfLooping() で移る
│ROM TCB list next StGroupVTable pointer 必須          │
│    StGroupVTable** m_ppStGroupVTableNex              │Delay/Ready Queue list RAM pointer
├───────────────────────────┤
│Looping Task TCB list next StGroupVTable pointer 選択 │Looping Queue list RAM pointer
│    StGroupVTable** m_ppStGroupVTableNext;            │
└───────────────────────────┘

struct StGroupVTable{
    struct StTaskBase* m_pStTaskBase;
    /* Redy Cue の Front 側にプッシュするタスクは優先順位の高い特別なタスクである */
    void(*m_pfPushFrntBk)(const struct StGroupVTable*);/*void(*m_pfDecrementGrp)(struct StTaskBase*);*/
    /* m_pfExecute() はタスク・パラメータを制御するので StGroupVTable* 引数が必要になる */
    void(*m_pfExecute)(const struct StGroupVTable*);

    /* この下は TmupCntxt.h/.c では使わない*/
    void(*m_pfLooping)(const struct StGroupVTable*);

    const struct StGroupVTable** m_ppStGroupVTableNext;
    const struct StGroupVTable** m_ppStGroupLoopingNext;

    /* 以下はアプリケーションによって変わってくる。*/
    void** m_ppVdPrm;
    TyByte  m_byPrmArSize; /* パラメータ配列サイズ */

    /* Stack には integer alignment がかかる機種がある。*/
    /* m_pByStack の初期値を設定するとき、この alignment がかかった値を設定する*/
    unsigned char* m_pByTaskStackBottom;   /*タスク・スタックつき Frame Context で使う */
    /* EnStart_ のとき TskCntxt_execute(.) のなかでタスク・スタックの初期化も行う*/
};




ダイナミック・ポーリング関数
struct StDynmcPllng{
    DfBool (*m_pfPolling)(void);
    const StDynmcPllng** m_ppStNextDynmcPllng;
    const StGroupVTable* m_pStGroupVTable
};

     pStOriginRamStt
       ┌──┐  ┏━━━━━━━━┓  ┏━━┓  ┌────────┐  
       │RAM ├→┃m_ppStNext      ┠→┃RAM ┠→│m_ppStNext      ├→
       └──┘  ┃m_pfPolling     ┃  ┗━━┛  │m_pfPolling     │  
                 ┃m_pStGroupVTable┃            │m_pStGroupVTable│  
                 ┃    ROM         ┃            │    ROM         │  
                 ┗━━┯━━━━━┛            └────────┘  
                       ↓
                    StGroupVTable

#ifdef DfTaskOOP_H_
// DynPlOOP.h is included after including TaskOOP.h
static const struct StDynmcPllng dfStDynmcPllngRom_ ={
         dfGnrtDynPllngCdInvlvFnctn
        ,&dfStDynmcPllngRam_
        ,&dfCstVTableStt_
    };
#else
static const struct StDynmcPllng dfStDynmcPllngRom_ ={
         dfGnrtDynPllngCdInvlvFnctn
        ,&dfStDynmcPllngRam_
        ,0
    };
#endif  //DfTaskOOP_H_
StDynmcPllng を使って Dynamic polling 関数のリストを管理する。リストにリンクすることで、ポーリング関数の実行を行うようになる。ただしユーザーが明示的に
	void DynmcPll_execute(void)
をメイン・ループから呼び出すことで、ポーリング関数の実行がなされる。

ダイナミック・ポーリング関数はウェート状態のタスクを再開させる条件判断を行わせるルーチンを実行させたり、実行させなかったりすることを目的に使える。再開の条件判断をしないときはリストから外しておくことで、無駄な CPU タイムの消費をなくすようにする。

タスクの再開条件を判断するポーリング・ルーチンはタスクがレディー状態になったときリンクから外すことが多いはずです。ポーリング・ルーチンがタスクをレディ状態にするとき、ポーリング関数の戻り値を false にすることで、ポーリング関数リストからの外すことができます。でもディレーのタイム・アップによりタスクがレディーになったときは、これではポーリング関数のアンリンクが行えません。そのために StDnmcPllin 構造体に m_pStGroupVTable メンバーを入れてあります。このポインタ・メンバーが 0 以外の値であることは、ポーリング関数がタスクと関連付けられていることを意味します。タスクがレディ状態になると自動的にポーリング関数リストからアンリンクされることを意味します。

タスク関数プロトタイプ
タスク関数のプロトタイプは下の型に限定します。ただしユーザーには dfTaskFnc(.) マクロを使って struct StDelayPcContext* 引数を明示的に見せません。コンパイラによってはスタック・オフセットの計算に、この二つの引数が必要になることがあるからです。
    void taskSample(struct StDelayPcContext* pStContextAg__, void* const pVdAg__)
common stack context switch
      │              │
      ├───────┤
      │return address│of PcCntxt_delayWait(
      ├───────┤ struct StDelayPcContext* ppStCntxtAg, TyWornd wdDelay)
┌─sp│ ppStCntxtAg  │
│    ├───────┤
│オ  │ wdDelayAg    │
│フ  ├───────┤
│セ  │auto variable │
│ッ  ├───────┤
│ト  │      .       │
│    ├───────┤
│    │auto variable │
│    ├───────┤
│    │EBP           │
│    ├───────┤                                        
│    │PcCntxt_return2Task(.)  or task 開始呼び出し側への return address    
│    ├───────┤                                        
└→●│pStDelayPcContextAg of PcCntxt_return2Task(.) ppStCntxtAg
      ├───────┤                                      
      │inArg 0 of  pVdArDamieAt[0]
      │───────┤                                      │
      │inArg 0 of  pVdArDamieAt[1]
      │───────┤                                      │
      │inArg 0 of  pVdArDamieAt[2]
      ├───────┤                                      │
スタック・フレームのタスク・コンテキストの一部として保存するとき &ppStCntxtAg - sp を使う。VC++ のときでも bp - sp ではない。bp の位置は &pStCntxtAg - 8 に固定されているので、bp の sp に対する相対位置を計算でだせます。こうしておけば、VC 以外の bp を使わないコンパイラ系でも同様な考え方で記述できます。

sp の位置を call 命令の後の add sp, #n の #n 分だけ減らす方法もある。しかし、これはコンパイラ依存部分を多くする。コンパイラ・オプションの付け方だけでもコードの変更が必要になる確率が高くなる。スタック上の戻りアドレス直前の位置をスタック使います。

スタック・フレーム・オフセットは StPcContext::m_ppStCnxtt_sp 変数に保存する。この名称は、コンパイラごとに変わりうる。コンテキスト・データ自体がコンパイラ毎に変わるからです。
78K4 スタック・フレームの保存
78K4 コンパイラではベース・ポインタを使わずに、コンパイラ・コードを SP からのオフセットのみから定めるコードを作らせることができます。このとき pStCntxtAg の位置は、スタックの最上位に詰まれます。タスク関数のオート変数より上の位置となります。このため、&pStCntxtAg をスタック・フレームのボトムにできません。

    sp│pStCntxtAg  push whl でスタックにつむ: 3 byte
   O↑├───────┤                            up=ax:pStDelayPcContextAg
   F││auto variable │                            whl == 
   F││   of task    │                          
   S││              │                          
   E││              │                           
   T││              │                          
    │├───────┤←───────────→ taskSample(StTcb stTcbAg__, pVdPrm)
    ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
    │├───────┤
  ●↓│pVdPrmAt      │ uup==pVdDamiAt, scheduler 側の auto 変数
      ├───────┤
      │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
      ├───────┤
      │ rp3          │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
      ├───────┤壊れるかもしれないのでスタックにのせる
      │              │

スタック・フレームを保存する操作も PcCntxt_delayWait(.) の中では行いません。そのためには &pVdPrmAt を PcCntxt_delayWait() 関数に渡す必要が出てきます。そのぶんだけ、ROM を消費してしまいます。タスク関数の最初にスタック・フレームを保存するコードをマクロで挿入しておきます。これはタスク関数の最初の一回だけで済み、コンテキスト・スイッチのたびにスタックフレームを作らずに済むので、ロムの消費を少なくできます。
#   define dfTaskFnc(taskSample, pVdAg__)\
        void taskSample(struct StDelayPcContext* pStContextAg__, void* const pVdAg__){\
            struct StDelayPcContext** ppStContextAt__;\ /* ROM code size を小さくするため */
            ppStContextAt__ = &pStContextAg__;\
            PcCntxt_setStackFrame(&pStContextAg__, &pVdAg__);