タスク関数プロトタイプ
タスク関数のプロトタイプは下の型に限定します。ただしユーザーには dfTaskFnc(.) マクロを使って struct StDelayPcContext* 引数を明示的に見せません。コンパイラによってはスタック・オフセットの計算に、この二つの引数が必要になることがあるからです。
    void taskSample(struct StDelayPcContext* pStContextAg__, void* const pVdAg__)
タイムアップ・タスクではコンテキスト・スイッチがありません。 pStContextAg__ を使わなくても済みます。でも k-uOS 全体の一様性を優先します。dfTaskFnc(smple,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__);
78K4 スタック・フレームの保存と Pascal 宣言
78K4 ではタスク関数の最初にスタック・フレームのオフセットをセーブしている。このため、PcCntxt_delayWait(.), PcCntxt_doLoopTimeup(.) を呼び出したと次のアドレスにスタック調整用に pop 命令がないことを前提にしている。x86 では add sp, #n があることを前提に記述してある

PcCntxt_call(struct StDelayPcContext** ppStCntxtAg, void(*pfAg)(.))
      ├───────┤
      │EBP           │←交換→pfAg また sp を、ここを指させて return する
      ├───────┤                                        
      │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]
      ├───────┤                                      │
PcCntxt_call(struct StDelayPcContext** ppStCntxtAg, void(*pfAg)(.)) at 78K
; line     1 : #include <PcCntxt.h>
; line     2 : int PcCntxt_ca( struct StDelayPcContext** ppStCntxtAg, void(*pfA
;              g)(struct StDelayPcContext* ) )
; line     3 : {
@@CODE	CSEG
_PcCntxt_ca:
$DGL	1,44
	push	up
	movw	up,ax
??bf_PcCntxt_ca:
; line     5 : 
; line     6 : #asm
$DGL	0,4
    ; 注意スタックの現在値より小さいアドレスにデータを設定しない
    
$DGL	0,6
    ; uup に pfAg の値を入れる
$DGL	0,7
    mov a,[sp+7]
$DGL	0,8
    mov u,a
$DGL	0,9
    movw ax,[sp+5]
$DGL	0,10
    movw up,ax

$DGL	0,12
    ; whl に return address の値を入れる
$DGL	0,13
    mov a,[sp+4]
$DGL	0,14
    mov w,a
$DGL	0,15
    movw ax,[sp+2]
$DGL	0,16
    movw hl,ax

$DGL	0,18
    ;uup, whl をずらしたスタック上の入れ替えた位置に設定する
$DGL	0,19
    ;add sp, #8
$DGL	0,20
    pop ax
$DGL	0,21
    pop ax
$DGL	0,22
    pop ax
$DGL	0,23
    pop ax 

$DGL	0,25
    push whl
$DGL	0,26
    push uup
$DGL	0,27
    ret
; line     7 :     ; 注意スタックの現在値より小さいアドレスにデータを設定しない
; line     8 :     
; line     9 :     ; uup に pfAg の値を入れる
; line    10 :     mov a,[sp+7]
; line    11 :     mov u,a
; line    12 :     movw ax,[sp+5]
; line    13 :     movw up,ax
; line    14 : 
; line    15 :     ; whl に return address の値を入れる
; line    16 :     mov a,[sp+4]
; line    17 :     mov w,a
; line    18 :     movw ax,[sp+2]
; line    19 :     movw hl,ax
; line    20 : 
; line    21 :     ;uup, whl をずらしたスタック上の入れ替えた位置に設定する
; line    22 :     ;add sp, #8
; line    23 :     pop ax
; line    24 :     pop ax
; line    25 :     pop ax
; line    26 :     pop ax 
; line    27 : 
; line    28 :     push whl
; line    29 :     push uup
; line    30 :     ret
; line    31 : #endasm
; line    32 :     return 0;   /* damie 0 to avoid errro */
$DGL	0,29
	subw	bc,bc
?L0002:
; line    33 : }
$DGL	0,30
??ef_PcCntxt_ca:
	pop	up
	ret
??ee_PcCntxt_ca:

@@BASE	CSEG	BASE

@@CALF	CSEG	FIXED
	END
のように up に ppStCntxtAg を入れてくれます。


      │              │
      ├───────┤
      │ up           │← sp 
      ├───────┤
[sp+2]│return address│←── PcCntxt_call(StDelayPcContext** ppStTCntxtAg, void(*pfAg)(.)) 
      ├───────┤
[sp+5]│ pfAg         │
      ├───────┤
      │pVdPrmAt      │ uup==pVdDamiAt, scheduler 側の auto 変数
      ├───────┤
      │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
      ├───────┤
      │ rp3          │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
      ├───────┤壊れるかもしれないのでスタックにのせる
      │              │
             ↓

      │              │
      ├───────┤
      │ pfAg         │
      ├───────┤← sp 
      │return address│←── PcCntxt_call(StDelayPcContext** ppStTCntxtAg, void(*pfAg)(.)) 
      ├───────┤
      │pVdPrmAt      │ uup==pVdDamiAt, scheduler 側の auto 変数
      ├───────┤
      │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
      ├───────┤
      │ rp3          │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
      ├───────┤壊れるかもしれないのでスタックにのせる
      │              │
--