Polling routine または Differed call を設ける。
----------
#include <DlyState.h>
#include <PcContex.h>
staic void polling(void) または #include <Differed.h>
#include <VTable.h>
---------
polling routine を使わない時は
#define DfUseMyDpc /* PcLoop.h にある polling() を使わない*/
voidpolling(){}
を作る。if ( pointerPolling ){ polling();} と記述するより、早いコードになる
C1┌────────┐ C6┌───────────┐
│StStateDelay │ │StVTable │
├────────┤ * ├───────────┤
│wdDelay:12bit │←───────◇│static StVTable m_list│
│m_enPCState:3bit│ │m_pContextVd │
│m_blReady:1bit │ │m_ppStVTableNext │
├────────┤ ├───────────┤
│setDelay() │ │m_pfPolling │
│DecrementDelay()│ │m_pfCheckAndExecute │
│setReturnState()│ │[checkAndExecute()] │
│terminate() │ │ │
└────────┘ └───────────┘
△
┌────────────┼─────────────┬────────────┐
┌────┴────┐┌──────┴─────┐┌──────┴─────┐┌─────┴─────┐
│ StTimeUpContext ││PcOOP ││PcLpOOP ││StJmpBfContext │
├─────────┤├────────────┤├────────────┤├───────────┤
│ m_pTimeUpAddress ││m_pTimeUpAddress ││m_pTimeUpAddress ││ │
│ ││mimimum compiler context││mimimum compiler context││jum_buf context │
├─────────┤├────────────┤├────────────┤├───────────┤
│ ││start() ││start() ││start() │
│ ││delay() ││delay() ││delay() │
│ ││restart() ││restart() ││restart() │
│ doAtTimeUp() ││checkAndExecute() ││checkAndExecute() ││checkAndExecute() │
└─────────┘└────────────┘└────────────┘└───────────┘
C2 C3 △ C4 △ C5
┌─────┴─────┐ ┌─────┴─────┐
│StStackContext │ │StStackContext │
├───────────┤ ├───────────┤
│int inArStack[] │ │int inArStack[] │
├───────────┤ ├───────────┤
C6│call(..) │C7│call(..) │
└───────────┘ └───────────┘
PcOOP.h はなくなった。DfUseMyDpc を宣言することで
PcLpOOP に PcOOP.h の役目をさせる。
1 StTimeUpContext では doAtTimeUp() を checkAndExecute() の変わりに使う。
StTimeUpContext では m_enCntxtState を使わない。doAtTimeUp() は呼び出
されても、コンテキスト・スイッチを行うことなく戻ってしまうからである。
2 task(StVTable* pStVTable) の形式とする。開始する時 pStVTable 変数を実際に設定する
3 context switch を伴う関数 pf を呼び出す時は
call(pf, StVTable* pStVTable, argv[1],argv[2],...)
と呼び出す。呼び出される context switch を伴う関数は
pf(StVTable* pStVTable, argv[1], argv[2],...)
のプロトタイプを持つ。
C3:stack context を共用する
コンテキスト・スイッチを伴うサブルーチンを使わない。
または コンテキスト・スイッチでは return の変わりに gotoMain() を使う
生々消滅するタスクを統一して扱う。(生成消滅はリストへの追加と削除で実装する。)
C++ ならば 継承、Has a 関係、strategy pattern で明確に記述できる構造が、が渾然と組み込まれている。
使用上の Tips
- task を開始する時にパラメータを渡したければ、StDelayAndContext の void 引数の最後に parameter 要素を追加する。
- IntCntxt で suspend(.)/restart(.) を共用するため、m_pStStateDelay が 0 であることを判定して、処理を分ける。
- terminate() は呼び出し元に戻ってくる。そうすることで汎用性を持たせる。C の return 命令によってコンテキスト・スイッチ側に戻す。
- task の this 引数 struct StDelayPcContext* pStDelayPcContextAg はスタックポインタの下限も示す。
コーディングでの注意点
- PcCntxt_start(struct StDelayPcContext*)、DlyState_setRestartState(., .) は割り込みからも呼べるようにする。そのために、terminate は m_enCntxtState と m_blReady を同時にクリアする。PcCntxt_start,DlyState_setRestartState は m_blReady を設定してから m_enCntxtState を設定する。
- ただし、スタック切り替えを必要としない StDelayPCContext だけに限定される。StJmpBfContext ではスタックを切り替えるので、割り込みルーチンからの呼び出しはできない。
- アプリケーション・タスク固有のスタティック変数に対するテスト・アンド・セットの狭間で発生するボラタイルなデータが発生しない保証はユーザーが付けることになる。一般的にはタスクが停止状態でない限り、割り込みから直接タスクの開始を呼び出すことは避けるべきである。
- startImmediately(), restartImmediately() によるタスクへの CPU 占有権の移動を活用する
- immediate call による別タスクの実行は CPU 占有権の合理的な分割に有利に働く
- 無駄に polling 部分を設けて CPU 時間を消費させるのは愚かしい
- immediately call 動作のほうが、デバッグ時、原因側への追求が容易である。
- immediately call 動作で CPU 負荷がが集中しすぎるときは通常の restart を使う。ただし戻り引数が 3 bit と短くなる
モニタの考え方
- task を終了するために明示的に DlyState_terminate(.) を呼び出す。タスク・サブルーチンの中で自分を終了させることもあるので、return だけで勝手に terminate させてはまずい。
- DlyState_terminate() は呼び出した側に戻ってくる。
- StJmpBfContext では terminate() は longjmp(.) を実行してコンテキストを checkAndExecute(.) 呼び出しの直前に戻す。
- PcCntxt_suspend( const struct StDelayPcContext** ppStAg); とする。ppStAg は suspend が戻るべきアドレスの存在位置を示すからである。<-- これは bp が示している。78K コンパイラとの互換性のためだけである。
- 現在 Run 中のタスクに対して、自分でしか delay, suspend を行えない。でも StPcCntxt* 引数を使う。
- 割り込みでのコンテキスト処理を分けねばならない
- 割り込みルーチンからの immediate task 起動もできなくなる
- KeyTask での immediateStart(.) を使う
- DlyState_whatState(.) は m_blReady, m_enCntxtState を一まとめにして返す。restart した後には wait 中の処理をさせないためである。
- EnStart によってタスクを開始するとき、または PcCntxt_startImmediately(.) によってタスクをもか縊死するとき、delay は 0 にされる。DlyState_setRestartState(.) または、PcCntxt_restartImmediately(.) によってリスタートさせるときは、delay は前の値が、残ったままである。再度 suspend(.) を実行すると、time up も働く。
suspend(StDelayPcContext**) call(←**)と引数を一つに限定する理由(
- 通常のコンテキスト・スイッチ以外に、割り込みルーチンでも呼び出される。
- task 関数自体を割り込みルーチンが呼び出すこともある。
- これらの動作をできるだけ多くの CPU/compiler で共通に動作するようにしておきたい。
- 応答 speed を犠牲にしても単純なコンテキスト・スイッチを選択する
- すべてのタスクでのスタック構造を同じにしておきデバッグを容易にする
df... を使う理由
dfDelay(30);-->
DlyState_setDelay((struct StDelayAndContext*)pStDelayPcContextAg,30);
PcCntxt_suspend( &pStDelayPcContextAg );
dfTerminate();->
DlyState_terminate((struct StDelayAndContext*)pStDelayPcContextAg)
のようなキャスティングをかけたり、Dly.___ の関数名を引っ張り出したりが面倒である。
スタックを共用するコンテキスト・スイッチ at VC5.0/6.0
基準位置 HLret2 は、割り込み処理の最中の相対位置として決まる。固定されない。 inDamieAt[] は必要に応じてユーザーが設定する。ユーザーが使用するタスク・サブルーチンの引数の数によって定まる。
← Interrupt Task Subroutine は StPcContext が局所化される。アプリケーションごとに StPcContext を継承して拡張できる。そこに引数の働きをする変数領域を定義できる。
DfIntrStart() intrTask(0) での SP データ構造
auto variable
ebp
return address
damie pStIntrCntxtAg
damie value
<-- IntCntxt_suspend(.) を呼び出す前に SP が差していた
TyByte PcCntxt_suspend( const struct StDelayPcContext** ) での SP データ構造
PcCntxt_suspend を呼ぶ前
EAX = 00408168 EBX = 7FFDF000 ECX = 0040A914
EDX = 00004003 ESI = 00000000 EDI = 00000000
EIP = 004063F8 ESP = 0012FF40 EBP = 0012FF48
EFL = 00000212 CS = 001B DS = 0023 ES = 0023
0x12ff34: タスクの di:0x000000
0x12ff34: タスクの si:0x000000
0x12ff34: タスクの bx:0x7ffdf000
0x12ff34: タスクの cx:0x12ff50 <-- mov cx,lea[pStDelayPcContextAg], push cx を行った後だ
0x12ff34: タスクの bp:0x12ff48
0x12ff38: return address: 0x00046401
0x12ff40: &pStDelayPcContextAg
TyByte PcCntxt_return2Task(const struct StDelayPcContext* pStDelayPcContextAg)
を呼ぶ前
EAX = 00000000 EBX = 7FFDF000 ECX = 0040BC7C
EDX = 0040BC7C ESI = 00000000 EDI = 00000000
00409168
EIP = 00401E5A ESP = 0012FF40 EBP = 0012FF58
EFL = 00000293 CS = 001B DS = 0023 ES = 0023
0x0012ff04: edi
0x0012ff08: esi
0x0012ff0c: ebx
ESP-->0x0012ff10: ESP -= -24
<-- auto variable 領域
EBP-->0x0012ff34: 0x0012ff58 == EBP <-- return2Task(.) は、この上で動作する
0x0012ff38: 0x00401e63 == return address
0x0012ff3c: 0x00409168 == pStDelayAndContextAg <-- &pStStackAndContextAg はここを指している
0x0012ff40: 0x???????? == inDamieAt[0] == 3
・ ・ ・
0x0012ff50: 0x???????? == inDamieAt[4] == 0
144: return 0; // damie return to avoid warning
参考
00401E12 xor al,al
00401E14 pop edi │
00401E15 pop esi │<-- この三つは __asm{...} を加えるだけで追加される
00401E16 pop ebx │
00401E17 mov esp,ebp
00401E19 pop ebp
00401E1A ret
mov eax,dword ptr[cppStAg] は ebp に対するオフセットとして判定されている。ebp の値をマニュアルで
変更すると、デバッガの変数が追従して変わってしまう。
│ │←-- m_pTimeUpAddress を入れる。
├───────┤ sp をこの位置に設定する ret でタスク側に context switch
0x12ff00 │ edi │ ←┐ sp =
├───────┤ │
│ esi │ │
├───────┤ │
│ ebx │ │← 0x4067ed m_pTimeUpAddress
├───────┤ │
0x12ff0c │pStPcContextAt │
├───────┤ │
│pStStateDelayAt │←タスク側に戻って add esp,4 を終わった後の sp
├───────┤ │ 但しタスク側では edi, esi, ebx の push はなく、
│byReturnValueAt │ inAt, chAt の二つのオート変数を使っている
├───────┤ │
│bp of PcCntxt_return2Task をここに移動する。== BPret2 │
├───────┤ │
│PcCntxt_return2Task(.) への return address │
(0x401eb2)├───────┤ │
0x12ff20●│pStDelayPcContextAg│of PcCntxt_return2Task(.) SPref2 │
(0x40bc40)├───────┤ ↓ │
│inDamieAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │これに対する相対位置に
├───────┤ │タスク側のデータを
│return address│ │設定する処理を、
├───────┤ │PcCntxt_return2Task()
│ │ が行う。
ここまでは task(.)
と同じスタック・フレーム
構造となる。
PcCntxt_return2Task()
を呼び出すことで作られ
る Stack データ構造。
EnStart)
├───────┤ │
│bp of PcCntxt_return2Task をここに移動する。== BPret2 │
├───────┤ │
│PcCntxt_return2Task(.) への return address │
(0x401eb2)├───────┤ │
0x12ff20●│pStDelayPcContextAg│of PcCntxt_return2Task(.) SPref2 │
(0x40bc40)├───────┤ ↓ │
│pVdDamieAt[3] │
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │これに対する相対位置に
void PcCntxt_saveContext(int* pInTaskSp では
pStPcContextAt->m_inEBP_ESP
= ((int)PcCntxt_ppStExecutingCntxt) - 8 - ((int)pInTaskSp);
と計算してスタック・フレームを保存する
return2Os(void) saveContext(const struct StDelayPcContext* pStDelayPcContextAg)
struct StDelayPcContext** PcCntxt_ppStExecutingCntxt を使ったときの
int PcCntxt_delay(TyWord wdDelayAg)
VC++ Dfcall(kc struct StStackAndContext**, ku(*pfAg)(.), arg1, arg2, arg3, arg4, arg5...
task stack
│ │
├───────┤
│User Auto Var │
├───────┤
│StPcContext │← pStPcContextAt
├─ ─┤
│ ・ │
├─ ─┤
│ │
├───────┤
│StPcContext │
├─ ─┤
│ ・ │
├─ ─┤
│ │
├───────┤
void StkCntxt_setCallPrm(,void(*pfAg)(const struct StStackAndContext*,...)
,const struct StStackAndContext** cppStStackAndContextAg,... )
│ │
├───────┤
│pBpPcCntxt_return2TaskAt ← SP
├───────┤
│bp │
├───────┤
│StkCntxt_setCallPrm(.) 呼び出し側への return address
├───────┤
│ pfAg
├───────┤
│←cppStStackAndContextAg of StkCntxt_setCallPrm(.)
├───────┤
││inArg 1 of StkCntxt_setCallPrm(.) │
││───────┤ │
││inArg 2 of StkCntxt_setCallPrm(.) │StkCntxt_setCallPrm()
copy│├───────┤ │を呼び出すことで作られ
┌─┤│inArg 3 of StkCntxt_setCallPrm(.) │る Stack データ構造。
│ │├───────┤ │
│ ││inArg 4 of StkCntxt_setCallPrm(.) │
│ │├───────┤ │
│ ││inArg 5 of StkCntxt_setCallPrm(.) │
│ ├───────┤ │
│ │ │
│
│
│ void StkCntxt_setCallPrm(void(*pfAg)(const struct StStackAndContext**,...)
│ ,const struct StStackAndContext** cppStStackAndContextAg,... )
│
│ ├───────┤
│ │ │
│ ├───────┤
│ │bp of PcCntxt_return2Task をここに移動する。== BPret2 ← これは setCallPrm で壊さない
│ ├───────┤ ← これも setCallPrm で壊さない
│ │PcCntxt_return2Task(.) への return address │
│ ├───────┤ │
│ ●│← cppStStackAndContextAg of pfAg(.),StkCntxt_setCallPrm(.)
│ ├───────┤ │ここまでは task(.)
│ ││inDamieAt[0] in PcCntxt_executeLoopPolling(.) │と同じスタック・フレーム
│ │├───────┤ │構造となる。
│ ││inDamieAt[1] in PcCntxt_executeLoopPolling(.) │
│ │├───────┤ │PcCntxt_return2Task()
└→││inDamieAt[2] in PcCntxt_executeLoopPolling(.) │を呼び出すことで作られ
│├───────┤ │る Stack データ構造。
││inDamieAt[3] in PcCntxt_executeLoopPolling(.) │
│├───────┤ │
││inDamieAt[4] in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
下は PcCntxt_return2Task(.) に属するスタック・フレームの構造である
EBP-->0x0012ff34: 0x0012ff58 == EBP <-- ここに pfAg を置いて return を実行する
0x0012ff38: 0x00401e63 == return address
0x0012ff3c: 0x00409168 == pStDelayAndContextAg <-- &pStStackAndContextAg はここを指している
0x0012ff40: 0x???????? == inDamieAt[0] == 3 <-- arg1 を設定する
・ ・ ・
0x0012ff50: 0x???????? == inDamieAt[4] == 0 <-- arg5 を設定する
TyByte PcCntxt_return2Task(const struct StDelayPcContext* pStDelayPcContextAg)
│ │
├───────┤
│auto variable │
├───────┤
│EBP │
├───────┤
│PcCntxt_return2Task(.) 呼び出し側への return address
├───────┤
●│pStDelayPcContextAg of PcCntxt_return2Task(.)
├───────┤
│inArg 1 of StkCntxt_setCallPrm(.) │
│───────┤ │
│inArg 2 of StkCntxt_setCallPrm(.) │StkCntxt_setCallPrm()
y ├───────┤ │を呼び出すことで作られ
│inArg 3 of StkCntxt_setCallPrm(.) │る Stack データ構造。
├───────┤ │
│inArg 4 of StkCntxt_setCallPrm(.) │
├───────┤ │
│inArg 5 of StkCntxt_setCallPrm(.) │
├───────┤ │
│ │
< -- chekAndExecute(.) の auto 変数領域を callTaskSub(.) の引数領域とみな
せるように、データを変換する
<-- DPC が入ったものを標準とする。連続ポーリングが必要ならば requestDPC
を最後に加える
<-- call(&pStStackAndCntxt, f,...) とする。
<-- 共通サブルーチンが使えるように 領域を確保しない #define と関数宣言だけの
<-- recursive like call を認めるためには専用の suspend が必要となる
ヘッダが必要となる
<-- ユーザーが確保したスタック範囲はユーザーが開放する
<-- debugger で stPcContextStt, cstStDelayPcContextStt の値が固定されているらしい
retrunFromTaskSub(kc struct StStackAndContext**, int)
dfCall(f, arg) を呼ぶ前のタスク・コンテキスト
EAX = 0040C000 EBX = 7FFDF000 ECX = 00406F3C
EDX = 00409218 ESI = 00000000 EDI = 00000000
EIP = 00406F4A ESP = 0012FF2C EBP = 0012FF34
EFL = 00000202 CS = 001B DS = 0023 ES = 0023
SS = 0023 FS = 0038 GS = 0000 OV=0 UP=0 EI=1 PL=0
ZR=0 AC=0 PE=0 CY=0
ここのコンテキストに戻すように 0x12ff34 スタックより 4+8 小さい位置に戻りアドレスを積み、bp=0x12ff34 に設定して ret を実行する。
真ん中の 4 は ret 命令分の 4 byte である
二つ目の 8 は StkCntxtCall(.) の次に add sp,8 がある分の補正である
MS VC5.0 コンパイラでの割り込みタスク・コンテキス・スイッチ
IntCntxt_suspend(pStIntrCntxtAg) ←PcCntxt_suspend(.) を使うでの SP データ構造
│ │
├───────┤
│auto variable │← sp
├───────┤
├───────┤
│auto variable │
├───────┤
│EBP │← bp
├───────┤
│InterruptTask(.) 呼び出し側への return address
├───────┤
●│pStDelayPcContextAg of PcCntxt_return2Task(.)
├───────┤
│ │
edi <-- esp==0x12fd58
esi
ebx
ebp <-- ebp==0x12fd64
return address
pStIntrCntxtAg
<-- IntCntxt_suspend(.) を呼び出す前に SP が差していた
add ebp, [ebp]
mov esp, ebp
pop ebp
ret
を IntCntxt_suspend(.) の最後で実行することで DfIntTask() からの戻りと
同じ操作をする
VC::IntCntxt_suspend(pStIntrCntxtAg, pByDamieAg) での SP データ構造
edi <-- esp==0x12fd58
esi
ebx
ebp <-- ebp==0x12fd64
return address
pStIntrCntxtAg
pByDamieAg
<-- IntCntxt_suspend(.) を呼び出す前に SP が差していた
add esp,8
mov esp, ebp
pop ebp
ret
を IntCntxt_suspend(.) の最後で実行することで DfIntTask() からの戻りと
同じ操作をする
MS VC5.0/6.0 コンパイラでの割り込みタスク・サブルーチン
スタック・フレーム、戻りアドレス
void StkCntxt_setCall0(void(*pfAg)(const struct StStackAndContext*
,const struct StStackAndContext** cppStDelayPcContextAg )
│ │
├───────┤
│auto variable │← sp │
├───────┤ │pfAg stack frame
├───────┤ │
│auto variable │ │
├───────┤ │
│EBP │← bp │(cppStDelayPcContextAg)-2
├───────┤
│InterruptTask(.) 呼び出し側への return address
├───────┤
●│cppStDelayPcContextAg of PcCntxt_return2Task(.)
├───────┤
│inDamie:arg │
├───────┤
│ │
TyWord StkCntxt_call( struct StStackAndContext** cppStAg)
│ret address │== 0x40707f
├───────┤
│cppStAg:12ff20│
├───────┤
│chAt │← sp 0x12ff10
├───────┤ │
│inAt e │ │
├───────┤ │
│EBP │← bp │(cppStDelayPcContextAg)-2
├───────┤
│InterruptTask(.) 呼び出し側への return address
├───────┤
●│cppStDelayPcContextAg of PcCntxt_return2Task(.) ← 0x12ff20
├───────┤
│inDamie:arg │
├───────┤
│ │
void StkCntxt_returnFromTaskSub( struct StStackAndContext** cppStAg)
│ │
├───────┤
│auto variable │← sp │
├───────┤ │pfAg stack frame
├───────┤ │
│auto variable │ │
├───────┤ │
│EBP │← bp │(cppStDelayPcContextAg)-2
├───────┤
│InterruptTask(.) 呼び出し側への return address
├───────┤
●│cppStDelayPcContextAg of PcCntxt_return2Task(.)
├───────┤
│inDamie:arg │
├───────┤
│ │
78K4 コンテキス・スイッチ
mm model defalut 最適化条件でのコンテキスト・スイッチである。最適化やメモリ・モデルを変えると、スタックへのオート変数の積み方が変わる。それに伴いコンテキスト制御も変わってくる。
78K4 の C コンパイラでは、オート変数へのアクセスを [sp+n] で行う。x86 での ebp が存在しない。そのためスタック・フレームを &pVdPrmAg - &pStDelayPcContextAg で作る。dfSetStackFrame(pVdPrmAg) をタスクの最初に実行する。dfTask(.) マクロに含めることも可能だが、関数を開始する'{' が一つ隠れてしまう。これを使っているスクリプトもある。関数を開始する '{' を残すほうを優先して、明示的にユーザが dfSetStackFrame(.) を書く。
x86 コンパイラでは dfSetStackFrame(pVdPrmAg) を空文にする。
78K4 関数スタック
_PcCntxt_checkAndExecute:
78K4
タスクの開始、dfFunction(taskSample.)、 dfCall(taskSample) を実行してタスク関数に移って
直後、まだタスクのスタックフレームを作る前のスタックの様子
├───────┤←───────────→ 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 を保存しない。どこかで
├───────┤壊れるかもしれないのでスタックにのせる
│ │
auto 変数の数によってコンパイラがスタック・フレームを作り上げ下のように配置する
●│pStTcbAg__ 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 を保存しない。どこかで
├───────┤壊れるかもしれないのでスタックにのせる
│ │
mm medium
│ │
├───────┤
│ up │← sp, uup:pVdDamieAt, ax==pStDelayPcContextAg
├───────┤
●│ vp │ :pStStateDelayAt
│├───────┤
││ rp3 │ :inReturnValueAt
│├───────┤
││return address│ of return2Task() and m_pTimeUpAddress(),
│├───────┤ whl = pStDelayPcContextAg
││ vp │← sp, uup:pVdDamieAt == pStDelayPcContextAg
├───────┤ vvp = pStDelayPcContextAg->m_pVdPrm
│ up: │ ==pStDelayPcContextAg
├───────┤
│return address│ and whl = pStDelayPcContextAg of _PcCntxt_checkAndExecute()
├───────┤
│pVdDamiAt │ scheduler 側の auto 変数
├───────┤
│ ・ │
├───────┤
│arg n │
mm medium
│ │
├───────┤
0xdfdd●│ ax │←sp :pStDelayPcContextAg
│├───────┤
││ rp3 │ :inReturnValueAt
│├───────┤
0xdfe1││return address│ of return2Task() and m_pTimeUpAddress(),
││ │
│├───────┤ ax = pStDelayPcContextAg
││ vp │vvp = pStDelayPcContextAg->m_pVdPrm a← m_enState
├───────┤
│ up: │uup:pVdDamieAt == pStDelayPcContextAg
├───────┤
0xdfe8│ vp │← sp
├───────┤
│ up: │
├───────┤
│return address│ and whl = pStDelayPcContextAg of _PcCntxt_checkAndExecute()
├───────┤
│pVdDamiAt │ scheduler 側の auto 変数
├───────┤
│ ・ │
├───────┤
│arg n │
ml large
│ │
├───────┤
│ vvp │← sp, uup:pVdDamieAt == pStDelayPcContextAg
├───────┤
│ uup │aut variable of task
├───────┤
│return address│ of return2Task() and m_pTimeUpAddress(),
├───────┤ whl = pStDelayPcContextAg
│ vvp │← sp, uup:pVdDamieAt == pStDelayPcContextAg
├───────┤ vvp = pStDelayPcContextAg->m_pVdPrm
│ uup │
├───────┤
│return address│ and whl = pStDelayPcContextAg of _PcCntxt_checkAndExecute()
├───────┤
│pVdDamiAt │ scheduler 側の auto 変数
├───────┤
│ ・ │
├───────┤
│arg n │
static dfTaskFnc(task, pVdPrmAg) と PcCntxt_suspend(&pStDelayPcContextAg)
mm medium memory model
│ │
├───────┤
│&pStDelayPcContextAg push ax でスタックにつむ:2 byte ← sp
├───────┤
│return address│ of PcCntxt_suspend(.):3 byte
├───────┤
│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑│───────┤
F││auto │
F││ viriable │
S│├───────┤
E││return address│ and whl = pStDelayPcContextAg of task()
T↓├───────┤
│pVdPrmAt │ == pVdDamiAtscheduler 側の auto 変数
├───────┤
│ ・ │
├───────┤
│arg n │
ml large memory model
│ │
├───────┤
│&pStDelayPcContextAg push whl でスタックにつむ:3 byte ← sp
├───────┤
│return address│ of PcCntxt_suspend(.):3 byte
├───────┤
│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑│───────┤
F││auto │
F││ viriable │
S│├───────┤
E││return address│ and whl = pStDelayPcContextAg of task()
T↓├───────┤
│pVdPrmAt │ == pVdDamiAtscheduler 側の auto 変数
├───────┤
│ ・ │
├───────┤
│arg n │
return2Task(.) at PcCntxt.c
return2Task(.) のアッセンブラ部分は PcCntxt.c と IntCntxt.c を同じにできない。使うオート変数の数がことなるので、オフセット補正が異なるからだ。戻り値を return2Task(.)::PcCntxt.c が使う点でも、異なる。
static void return2Task( struct StPcContext* pStPcContextAstruct)
at PcCntxt.c and PcCntxt_exeuteLoopPolling()
mm medium
│ │
├───────┤
0xdfdd │ up │←sp up:pStDelayPcContextAg
├───────┤
0xdfdf●│ vp │ vp:pStDelayAt
│├───────┤
││ rp3 │ rp3:inReturnValueAt
│├───────┤
0xdfe3││return address│ of return2Task() and m_pTimeUpAddress(),
││ │
│├───────┤
0xdfe6│ hl:00 │← sp pVdDamiAt
├───────┤
│ ax: │stStateDelayAt
├───────┤
│ up: │
├───────┤
0xdfec│return address│ and ax = pStDelayPcContextAg of PcCntxt_exeuteLoopPolling()
│ 0xc643 │
├───────┤
│arg n │
static void return2Task( struct StDelayPcContext* pStDelayPcContextAg)
at PcCntxt.c
│ │
├───────┤
│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑├───────┤ ├───────┤ uup=whl:pStDelayPcContextAg
F││auto │of task │push register │ vvp==pStStateDelayAt
F││ viriable │ │ up │ :pStDelayPcContextAg
S││ │ │ vp │ :pStStateDelayAt
E││ │ │ rp3 │ :inReturnValueAt
T│├───────┤ ├───────┤
││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
↓├───────┤
│pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
│uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
├───────┤
│ rp3 │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
├───────┤壊れるかもしれないのでスタックにのせる
│ │
int PcCntxt_call( struct StDelayPcContext** ppStAg)
PcDfcOOP.h::dfFunction(pfAg,pVdPrmAg)
SkIntOOP.h::dfCall( pfAg, pVdPrmAg__)
SkDfcOOP.h::dfCall( pfAg, pVdPrmAg__),dfFunction(pfAg,pVdPrmAg)
でスケジューラーのスタック・フレームと、タスクのスタックフレームを重ねあわせる
ppStAg が ● を指している。ppStAg->m_inStackFram だけ先に pVdPrmAg がある。
その直前に戻りアドレスが存在している。
すでに ppStAg->m_inStackFram の設定は dfSetStackFrame(pVdPrmAg) で終わっている
mm medium memory model
│ │
├───────┤
│return address│ of PcCntxt_call
├───────┤
●│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑├───────┤ up=ax:pStDelayPcContextAg
F││auto variable │ whl ==
F││ of task │
S││ │
E││ │
T│├───────┤
││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
↓├───────┤
│pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
│uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
├───────┤
│ rp3 │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
├───────┤壊れるかもしれないのでスタックにのせる
│ │
ml large memory model
ppStAg が ● を指している。ppStAg->m_inStackFram だけ先に pVdPrmAg がある。
その直前に戻りアドレスが存在している。
すでに ppStAg->m_inStackFram の設定は dfSetStackFrame(pVdPrmAg) で終わっている
│ │
├───────┤
│return address│ of PcCntxt_call
├───────┤
●│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑├───────┤ uup=whl:pStDelayPcContextAg
F││auto variable │ vvp==pStStateDelayAt
F││ of task │
S││ │
E││ │
T│├───────┤
││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
↓├───────┤
│pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
│uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
├───────┤
│ rp3 │PcCntxt_checkAndExecute(.) が rp3 を保存しない。どこかで
├───────┤壊れるかもしれないのでスタックにのせる
│ │
void StkCntxt_setCallPrm(struct StStackAndContext** ppStStackAndContextAg
,int(*pfAg)(struct StStackAndContext*, void*), void* pVdAg )
mm memory model のとき
ppStStackAndContextAg が ● を指している。ppStAg->m_inStackFram
だけ先に pVdPrmAg がある。その直前に戻りアドレスが存在している。
uup == ppStIntStackCntxtVdPrmAg である
│ up │← sp uup=ppStStackAndContextAg, rp3:pStTaskStackContextDataAt
├───────┤
│ vp │
├───────┤
│ rp3 │
├───────┤
│return address│ of SkIntCnt_setCallPrm(.)
├───────┤
│ pfAg │
├───────┤
│ pVdPrmAg │← [sp+12]
├───────┤
m O●│ ax:pStPcContextAg__ push でスタックにつむ: 3 byte
_ F↑├───────┤ uup=whl:pStDelayPcContextAg
i F││ up │
n S│├───────┤
S E││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
t T↓├───────┤
a │pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
c ├───────┤
k │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
F ├───────┤
r │ │
a
m
e
ppStStackAndContextAg が ● を指している。ppStAg->m_inStackFram
だけ先に pVdPrmAg がある。その直前に戻りアドレスが存在している。
uup == ppStStackAndContextAg である
│ │
├───────┤
│ vvp │← sp uup=ppStStackAndContextAg
├───────┤
│ uup │
├───────┤
│return address│ of StkCntxt_setCallPrm(.)
├───────┤
│ pfAg │
├───────┤
│ pVdAg │
├───────┤
●│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
m O↑├───────┤ uup=whl:pStDelayPcContextAg
_ F││auto variable │ vvp==pStStateDelayAt
i F││ of task │
n S││ │
S E││ │
t T│├───────┤
a ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
k ↓├───────┤
F │pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
r ├───────┤
a │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
m ├───────┤
e │ │
タスク・スタック・データ
│ │
├───────┤
│m_pVdPrm │
├───────┤
││m_pfTimeup │3 byte ← m_pInStack │m_stPcContext│5 byte
│├───────┤ │m_stPcContext│
││m_inStackFrame│2 byte │m_stPcContext│
│├───────┤
││m_pVdPrm │3 byte
├───────┤
││m_pfTimeup │
│├───────┤
struct StStackAndContext{
struct StPcContext m_stPcContext; /* 5byte: m_pfTimeup m_inStackFrame*/
void* m_pVdPrm;
struct StStateDelay m_stStateDelay;
/* m_stPcContext, m_stStateDelay, m_pVdPrm までは StDelayPcContext と同じ*/
int* m_pInStack;
}
static dfTaskFnc(test,pVdPrmAg)
{
dfSetStackFrame(pVdPrmAg);
dfCall(test,9);
}
をコンパイルすると、下のコードを吐き出す。
; line 629 : static dfTaskFnc(test,pVdPrmAg)
; line 630 : {
_test:
$DGL 1,411
push whl
??bf_test:
; line 631 : dfSetStackFrame(pVdPrmAg);
$DGL 0,2
movg whl,[sp+6] ; pVdPrmAg
call !!_dfSetStackFrame
; line 632 : dfCall(test,9);
$DGL 0,3
movg whl,#09H ; 9
push whl
movg whl,#_test
push whl
movg whl,sp
addg whl,#06H
call !!_StkCntxt_setCallPrm
addwg sp,#06H ; 6 ; 5 byte 命令 addg rg,#
movg whl,sp ; 2 byte 命令 movg rg,rg
call !!_PcCntxt_call ; 4 byte 命令 call !!
; line 633 : }
$DGL 0,4
??ef_test:
pop whl
ret
??ee_test:
(*ppStStackAndContextAg)->m_pVdPrm = *(void**)(ppStStackAndContextAg + 1);
の式を使っても 78K4 では TCB に pVdDamiAt in scheduler を保存できない
void StkCntxt_returnFromTaskSub( struct StStackAndContext** ppStAg)
既に StkCntxt_setReturnValue(.) により pVdDamieAt に戻り値が仮値として設定されている。StkCntxt_returnFromTaskSub(.) は タスク・スタック上 pVdDamieAt を戻り値として bc register に入れる。の m_pVdPrm の値をスケジューラーの pVdDamieAt を入れる
インライン・アッセンブラ・コードに入ったとき vvp には m_pInStack の値が入っている。tde には StStackAndContext** ppStAg の (*ppStAg) が入っている。
サブルーチン側のスタック・フレームから pVdDamieAt 位置を計算し、タスク・スタックにある m_inStackFrmae 値より、戻る側の ● &pStStackAndContextAg を計算する
mm medium memory model
│ │
├───────┤
│ up │← sp uup=ppStStackAndContextAg ( ppStAg )
├───────┤ vvp: pStPcContextAt = (*ppStStackAndContextAg)->m_inStack+8
│ vp │ tde: *ppStStackAndContextAg (*ppStAg)
├───────┤
│return address│ of StkCntxt_returnFromTaskSub(.)
├───────┤
●│pStStackAndContextAg push ax でスタックにつむ: 2 byte
O│├───────┤ uup=whl:pStDelayPcContextAg
F││auto variable │ vvp==pStStateDelayAt
● F││ of task │
↑ S││ │
│ E││ │
│ T│├───────┤
│ ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
│ ↓├───────┤
│pVdDamieAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
│uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
├───────┤
│ │
struct StStackAndContext{
struct StPcContext m_stPcContext; /* 5byte: m_pfTimeup m_inStackFrame*/
void* m_pVdPrm;
struct StStateDelay m_stStateDelay;
/* m_stPcContext, m_stStateDelay, m_pVdPrm までは StDelayPcContext と同じ*/
int* m_pInStack;
ml large memory model
│ │
├───────┤
│ vvp │← sp uup=ppStStackAndContextAg
├───────┤ vvp = (*ppStStackAndContextAg)->m_inStack+8
│ uup │ tde = *ppStStackAndContextAg
├───────┤
│return address│ of StkCntxt_returnFromTaskSub(.)
├───────┤
●│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O│├───────┤ uup=whl:pStDelayPcContextAg
F││auto variable │ vvp==pStStateDelayAt
● F││ of task │
↑ S││ │
│ E││ │
│ T│├───────┤
│ ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
│ ↓├───────┤
│pVdDamieAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
│uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
├───────┤
│ │
struct StStackAndContext{
struct StPcContext m_stPcContext; /* 5byte: m_pfTimeup m_inStackFrame*/
void* m_pVdPrm;
struct StStateDelay m_stStateDelay;
/* m_stPcContext, m_stStateDelay, m_pVdPrm までは StDelayPcContext と同じ*/
int* m_pInStack;
mm memory model のとき
dfStartIntStt(task,0) ---- dfWaitInt()
├───────┤
0xe0e6│&pStPcContextAg:0xebeb
├───────┤
0xe0e8│return address│PcCntxt_suspend
│ 0xe0015b │
├───────┤
│0xe0eb│pStPcContextAg│
│ ├───────┤
5 │0xe0ed│return address│task(.) of remocon.c
│ │ 0xb0c6ce │
│ ├───────┤
│0xeof0│ 00 pVdAg of (*pfAg)(pStAg,pVdAg) and AX = 0xe100:&stPcContextStt
├───────┤IntCntxt_startImmediately(pStPcContextStt, 0) が (*pf)(.) と呼び出す
│ up:rp5 │
├───────┤
0xe0f4│ vp:rp4 │
mm memory model のとき
IntCntxt_restartImmediately((pStPcContesxtAg,pVdRestartAg)
-- restart2Task(pStPcContesxtAg) ---- dfWaitInt()
├───────┤
0xe0e6│&pStPcContextAg:0xebeb
├───────┤
0xe0e8│return address│PcCntxt_suspend
│ 0xe0015b │
├───────┤
│0xe0e9│pStPcContextAg│==0xe100
│ ├───────┤
5 │0xe0eb│return address│task(.) of remocon.c
│ │ 0xb0c6ce │
│ ├───────┤
│0xeoee│ 01 pVdAg of IntCntxt_restartImmediately(pStPcContextAg, pVdRestartAg)
├───────┤
0xe0f0│ up:rp5 │
├───────┤
0xe0fe│ vp:rp4 │
├───────┤
│ │
ml memory model のとき
ppStStackAndContextAg が ● を指している。ppStAg->m_inStackFram
だけ先に pVdPrmAg がある。その直前に戻りアドレスが存在している。
uup == ppStStackAndContextAg である
│ │
├───────┤
│ vvp │← sp uup=ppStStackAndContextAg
├───────┤
│ uup │
├───────┤
│return address│ of StkCntxt_setCallPrm(.)
├───────┤
│ pfAg │
├───────┤
│ pVdAg │
├───────┤
●│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
m O↑├───────┤ uup=whl:pStDelayPcContextAg
_ F││auto variable │ vvp==pStStateDelayAt
i F││ of task │
n S││ │
S E││ │
t T│├───────┤
a ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
k ↓├───────┤
F │pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
r ├───────┤
a │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
m ├───────┤
e │ │
static void return2Task( struct StDelayPcContext* pStDelayPcContextAg) at IntCntxt.c
m_stStateDelay への設定と return value の設定がないことを除いて return2Task(.) at PcCntxt.c と同じである。
void SkIntCnt_setCallPrm(struct StIntStackCntxtVdPrm** ppStIntStackCntxtVdPrmAg
,int(*pfAg)(struct StIntStackCntxtVdPrm*, void*), void* pVdAg )
割り込みではない StkCntxt_setCallPrm(.) とは
- StTaskStackContextData ←→ StIntCntxtVdPrmDamie
- StStackAndContext ←→ StIntStackCntxtVdPrm
と、TCB が異なっている。それ以外は同じである。アッセンブラ部分は全く同じである
mm memory model のとき
ppStStackAndContextAg が ● を指している。ppStAg->m_inStackFram
だけ先に pVdPrmAg がある。その直前に戻りアドレスが存在している。
uup == ppStIntStackCntxtVdPrmAg である
│ up │← sp uup=ppStIntStackCntxtVdPrmAg, rp3:pStIntCntxtVdPrmDamieAt
├───────┤
│ vp │
├───────┤
│ rp3 │
├───────┤
│return address│ of SkIntCnt_setCallPrm(.)
├───────┤
│ pfAg │
├───────┤
│ pVdPrmAg │← [sp+12]
├───────┤
m O●│ ax:pStPcContextAg__ push でスタックにつむ: 3 byte
_ F↑├───────┤ uup=whl:pStDelayPcContextAg
i F││ up │
n S│├───────┤
S E││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
t T↓├───────┤
a │pVdPrmAt │ uup==pVdDamiAt, scheduler 側の auto 変数
c ├───────┤
k │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
F ├───────┤
r │ │
a
m
e
SkIntCnt _returnFromTaskSub(.) についても、TCB データ構造が異なる以外は StkCntxt_returnFromTaskSub(.) と同じである
void SkIntCnt_returnFromTaskSub( struct StIntStackCntxtVdPrm** ppStAg)
mm medium, default option
│ │
├───────┤
0xdfe2│ up │← sp up=ppStAg ( ppStAg )
├───────┤ vvp: pStPcContextAt = (*ppStStackAndContextAg)->m_inStack+7
│ vp │ tde: *ppStStackAndContextAg (*ppStAg)
├───────┤
0xdfe6│return address│ of StkCntxt_returnFromTaskSub(.)
│ │
├───────┤
0xdfe9●│pStStackAndContextAg push ax でスタックにつむ: 2 byte
O│├───────┤ uup=whl:pStDelayPcContextAg
F││auto variable │ vvp==pStStateDelayAt
● F││ of task │
↑ S││ │
│ E││ │
│ T│├───────┤
│ ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
│ ↓├───────┤
┌─0xdfee│pVdDamieAt │ uup==pVdDamiAt, scheduler 側の auto 変数
│ ├───────┤
│ │uup:pStDelayPcContextAg of PcCntxt_checkAndExecute(.) アッセンブラで積む
│ ├───────┤
│ │ │
│
│スタック上に残してあった戻った側のスタック・フレーム値で、スタック・フレームを再構築する
│
│ ├───────┤
│ 0xdfe6│return address│
│ │ │
│ ├───────┤
│0xdfe9●│pStStackAndContextAg push ax でスタックにつむ: 2 byte
│ O│├───────┤ uup=whl:pStDelayPcContextAg
│ F││auto variable │ vvp==pStStateDelayAt
│ ● F││ of task │
│ ↑ S││ │
│ │ E││ │
│ │ T│├───────┤
│ │ ││return address│ and whl = pStDelayPcContextAg of,return2Task(), task()
│ │ ││ │
│ │ ↓├───────┤
└→0xdfee│pVdDamieAt │ uup==pVdDamiAt, scheduler 側の auto 変数
├───────┤
dfCallInt(.) がタスク・スタックに保存する再開アドレスは 0x036e
mm medium memory model and default optiomaization
├───────┤up = ppStIntStackCntxtVdPrmAg
0xdfd8│ up: │← sp up,vp は SkIntCnt_setCallPrm() の woking arear
├───────┤
│ vp: │
├───────┤
│ rp3 │
├───────┤ │
0xdfde│return address│of SkIntCnt_setCallPrm(.) │
0xe151│ :0x0362 │ │
├───────┤ ├──────────
│detectBusFree() function address 0xe154│&pStPcContextAg__:0xe159
│ :0x2a1 │ ├─────────
├───────┤ 0xe156│return address :0x00031c
│ pVdAg:0x0000 │ │of PcCntxt_suspend()
┌────├───────┤ ├─────────
│●0xdfe6│ ax:pStPcContextAg__ ← sp 0xe159│ax:pStPcContextAg__:0xe10e
│ 0xe159├───────┤ of makeFreeBusState(.) ├─────
│ 0xdfe8│return address│of makeBusFreeState(.) │
│ 0xe15b│ 0x0c6a2:0xb7a3 │ │
│ ├───────┤
│ 0xdfeb│ pVdPrmAg │ of IntCntxt_startImmediately()
│ 0xe15e├───────┤
│ │ up: │up,vp は IntCntxt_startImmediately() の woking arear
│ ├───────┤
│ │ vp: │
│ ├───────┤
│ 0xdff1│return address│of IntCntxt_startImmediately(.)
│ 0xe164│ 0x00785 │
│ ├───────┤
│ 0xe167│makeBusFreeState()
│ │ :0x32e │
│ ├───────┤
│ 0xe16a│pVdAg:0x0000 │
│ ├───────┤
│
│
│ 0xdfe0 から 0xdfe2 に timeup address を代入して、そっちに ret 命令を使って移る
│ ├───────┤up = ppStIntStackCntxtVdPrmAg
│ 0xdfe1│ up: │← sp. up は PcCntxt_call() の woking arear
│ 0xe154├───────┤
│ 0xdfe3│return address│of PcCntxt_call(.)
│ 0xe156│ :0x0362 │
│ ├───────┤
│●0xdfe6│ ax:pStPcContextAg__ ← sp
0xe159├───────┤ of makeFreeBusState(.)
├───────┤up = ppStIntStackCntxtVdPrmAg
0xdfd8│ up: │← sp up,vp は SkIntCnt_setCallPrm() の woking arear
├───────┤
│ vp: │
├───────┤
│ rp3 │
├───────┤
0xdfde│return address│of SkIntCnt_setCallPrm(.)
│ :0x0362 │
├───────┤
│detectBusFree() function address
│ :0x2a1 │
├───────┤
│ pVdAg:0x0000 │
┌────├───────┤
│●0xdfe6│ ax:pStPcContextAg__ ← sp
│ ├───────┤ of makeFreeBusState(.)
│ 0xdfe8│return address│of makeBusFreeState(.)
│ │ 0x0c6a2 │
│ ├───────┤
│ 0xdfeb│ pVdPrmAg │ of IntCntxt_startImmediately()
│ ├───────┤
│ │ up: │up,vp は IntCntxt_startImmediately() の woking arear
│ ├───────┤
│ │ vp: │
│ ├───────┤
│ 0xdff1│return address│of IntCntxt_startImmediately(.)
│ │ 0x00785 │
│ ├───────┤
│ │makeBusFreeState()
│ │ :0x32e │
│ ├───────┤
│ │pVdAg:0x0000 │
│ ├───────┤
│
│
│ 0xdfe0 から 0xdfe2 に timeup address を代入して、そっちに ret 命令を使って移る
│ ├───────┤up = ppStIntStackCntxtVdPrmAg
│ 0xdfe1│ up: │← sp. up は PcCntxt_call() の woking arear
│ ├───────┤
│ 0xdfe3│return address│of PcCntxt_call(.)
│ │ :0x0362 │
│ ├───────┤
│●0xdfe6│ ax:pStPcContextAg__ ← sp
├───────┤ of makeFreeBusState(.)
return2Task(.) at IntCntxt.c
dfWaintInt(.) も PcCntxt_suspend(.) を使っている。PcCntxt_suspend(.) は int の戻り値をもつ。しかし割り込みルーチン内のタスクにはタイム・アップがない。タスク状態がない。PcCntxt_suspend(.) の戻り値は 1 に固定した。
static void return2Task( struct StPcContext* pStPcContextAstruct)
at IntCntxt.c
│ │
├───────┤
│pStDelayPcContextAg push whl でスタックにつむ: 3 byte
O↑├───────┤
F││auto │of task
F││ viriable │ ├───────┤uup←pStDelayPcContextAg
E││ │ │ up │
T│├───────┤ ├───────┤
0xdfeb│return address│ and ax = pStDelayPcContextAg of return2Task(){..}
│ 0x0c6d7 │
↓├───────┤
0xdfee│ pVdPrmAg:2 │
├───────┤
│ up: │ working of IntCntxt_restartImmediately()
├───────┤
│ vp │
├───────┤
0xdff4│return address│of IntCntxt_restartImmediately()
│ 0x42ca │
├───────┤
│ │
│ │
setjmp(.)/longjmp(.)
rp3, RG4, RG5, RG7(PC), SP: 14 byte
78K0 コンテキス・スイッチ
78K0 関数スタック
関数の最初と最後
_CntxtGrp_checkAndExecute:
$DGL 1,43
push hl
push ax
movw ax,sp
movw hl,ax
??bf_CntxtGrp_checkAndExecute:
; line 9 : /* Round Robin 制御とする。そのための 1 word は安い */
; line 10 : static const struct StGroupVTable* cpStStt;
; line 11 :
; line 12 : for ( cpStStt = cpStGroupVTableOrgnStt; cpStStt != 0
$DGL 0,5
movw ax,!_cpStGroupVTableOrgnStt
movw !?L0003,ax ; cpStStt
?L0004:
movw ax,!?L0003 ; cpStStt
or a,x
bz $?L0005
・
・
・
・
movw ax,!_cpStGroupVTableOrgnStt
movw !?L0003,ax ; cpStStt
; line 33 : }
$DGL 0,26
??ef_CntxtGrp_checkAndExecute:
pop ax
pop hl
ret
│auto var │ ←----- sp, and hl
├───────┤ ↑
│ ・ │ │
├───────┤ │
│auto var │ │この差分をコンテキストに溜める。
├───────┤ │
│arg 1 in ax │ ←--------- pStDelayAndContext 引数
├───────┤ ↓
│hl │ --------
├───────┤
│return address│
├───────┤
│arg 2 │
├───────┤
│ ・ │
├───────┤
│arg n │
- sp と hl の差分は 0 になっている。PcCntxt に溜めるのは、古い hlを入れてあるスタック位置と現在のスタック位置の差分である。
- RTOS がタスクにコンテキスト・スイッチを切りかえる時は、pStDelayAndContext 引数をスタックに設定し、sp を溜めておいたスタック位置の差分だけずらしてから、プログラム・カウンタをコンテキストに溜めてあった値にする。
- 関数の内容が極端に単純なとき、呼び出し側の hl や arg1 引数 ax をスタックに入れないことがある。しかしタスク関数では delay(.), suspend(.), terminate(.) などの関数が arg1 pStDelayAndContext を使う。こめためスタックに呼び出し側の hl や arg1 引数 ax を溜めることが保証される。
PcCntxt_suspend(.) stack データ構造
│ │
├───────┤
│pStPcContextAt;←-- hl at PcCntxt_suspend(.)
├───────┤
│pStStateDelayAt;
├───────┤
┌──┤ppStDelayPcContextAg
│ ├───────┤
│┌─┤hl │
││ ├───────┤
││ │task から PcCntxt_suspend(.) を読んだ時の return address
││ ├───────┤
│└→│task auto varN│-------
│ ├───────┤ ↑この差分を PcCntxt TCB に保存する
│ │ ・ │ │== ppStDelayPcContextAg - hl - 6*2
│ ├───────┤ │6 は pStPcContextAt, pStStateDelayAt,
│ │task auto var1│ │ ppStDelayPcContextAg, hl, ret addr,
│ ├───────┤ │ pStDelayPcContextAg
└─→│pStDelayPcContextAg │
├───────┤ ↓
│hl of PcCntxt_return2Task(.)←-- task(StDelayPcContext* )
├───────┤
│PcCntxt_return2Task(.) への return address
├───────┤
│inDamieAt[0] in PcCntxt_executeLoopPolling(.)
├───────┤
│inDamieAt[1] in PcCntxt_executeLoopPolling(.)
├───────┤
│inDamieAt[2] in PcCntxt_executeLoopPolling(.)
├───────┤
│inDamieAt[3] in PcCntxt_executeLoopPolling(.)
├───────┤
│inDamieAt[4] in PcCntxt_executeLoopPolling(.)
├───────┤
│pStStateDelayAt in PcCntxt_executeLoopPolling(.)
├───────┤
│return address│
├───────┤
│ │
PcCntxt_return2Task(.) stack データ構造
│ │←-- m_pTimeUpAddress を入れる。
├───────┤ sp をこの位置に設定する ret でタスク側に context switch
│ │ ←┐ hl = hl(of PcCntxt_return2Task)
├───────┤ │ - pStPcContextAt-> m_sp_hl
│ ・ │ │
├───────┤ │
│byReturnValueAt (at│[hl+1])←-- sp, hl
├───────┤ │
│pStPcContextAt│ │
├───────┤ │
│pStStateDelayAt │
├───────┤ │
●│pStDelayPcContextAg│of PcCntxt_return2Task(.) HLref2 │ここまでは task(.)
├───────┤ ↓ │と同じスタック・フレーム
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │構造となる。
├───────┤ │
│PcCntxt_return2Task(.) への return address │PcCntxt_return2Task()
├───────┤ │を呼び出すことで作られ
│inDamieAt[0] in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│inDamieAt[1] in PcCntxt_executeLoopPolling(.) │これに対する相対位置に
├───────┤ │タスク側のデータを
│inDamieAt[2] in PcCntxt_executeLoopPolling(.) │設定する処理を、
├───────┤ │PcCntxt_return2Task()
│inDamieAt[3] in PcCntxt_executeLoopPolling(.) │が行う。
├───────┤ │
│inDamieAt[4] in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
task(const struct StDelayPcContext* pStDelayPcContextAg)
TyByte PcCntxt_return2Task(const struct StDelayPcContext* pStDelayPcContextAg)
と task() の開始呼び出しで PcCntxt_return2Task() の呼び出しだ同じスタック・フレーム
を作ることを利用している
retrunFromTaskSub(kc struct StStackAndContext**, int) at 78K0
void StkCntxt_setCallPrm(const struct StStackAndContext** cppStStackAndContextAg,...)
│ │
├───────┤
│pfAt of StkCntxt_setCallPrm(.) ←-- hl,sp pointer
├───────┤
│hl of PcCntxt_checkAndExecute(.) をここに移動する。== HLret2
├───────┤
│StkCntxt_setCallPrm(.) への return address
├───────┤
│cppStStackAndContextAg of StkCntxt_setCallPrm(.)
├───────┤
┌─┼┤inArg of StkCntxt_setCallPrm(.) │ここまでは task(.) と
│ ├───────┤ │る Stack データ構造。
│ │ │
│
│
│ TyWord StkCntxt_call(const struct StStackAndContext** cppStStackAndContextAg
│ を実行することで作りあげるスタック・フレーム
│
│ ├───────┤
│ │ │
│ ●├───────┤HLref2
│ │pStStackAndContext ←cppStStackAndContextAg of pfAg(.)│ここまでは task(.)
│ ├───────┤ │と同じスタック・フレーム
│ ○│hl of PcCntxt_return2Task をここに移動する。== HLret2 │構造となる。
│ ├───────┤ │
│ │PcCntxt_return2Task(.) への return address │PcCntxt_return2Task()
│ ├───────┤ │を呼び出すことで作られ
└→ │inDamieAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
TyWord StkCntxt_call(const struct StStackAndContext** cppStStackAndContextAg
│ │
├───────┤
│pStPcContextAt ←-- hl,sp pointer
├───────┤
0xfb34│cppStAg of StkCntxt_call(.) │ここまでは task(.)
├───────┤ │と同じスタック・フレーム
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │構造となる。
├───────┤ │
0xfb38│StkCntxt_call(.) への return address │PcCntxt_return2Task()
├───────┤ │を呼び出すことで作られ
0xfb3a│chAt │ ←┐hl,sp │0x00: 1 byte
├───────┤ │ │
│inAt │ │
├───────┤ │ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0x0084
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44 ここに pfTaskSub を入れる。
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x0668
├───────┤ │を呼び出すことで作られ
0xfb44│inDamieAt:1 in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
│ │
StkCntxt_call(.) で ret命令を実行する直前のデータ配置
│ │
├───────┤
●│pStStackAndContext:0xfbcc
├───────┤
○│pfAg │← SP, hl==hl of PcCntxt_checkAndExecute(.),ax==pStStackAndContext
├───────┤
│PcCntxt_return2Task(.) への return address │
├───────┤ │
│inDamieAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │ここまでは task(.)関数が
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │スタックに積む
├───────┤ │
│return address│ │
├───────┤ │
│ │
TyWord StkCntxt_call( struct StStackAndContext** cppStAg)
│ │
├───────┤
│pStPcContextAt│←┐hl,sp at PcCntxt_suspend(.)
├───────┤
│pStStateDelayAt
├───────┤
0xfb34│ppStDelayPcContextAg │0xfb3e
├───────┤
│HL │ │0xfb3a
├───────┤
0xfb38│return address of PcCntxt_suspend(.) │0x083f
├───────┤
0xfb3a│chAt │ ←┐hl,sp │0x00: 1 byte
├───────┤ │ │
│inAt │ │
├───────┤ │ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0x0084
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x0668
├───────┤ │を呼び出すことで作られ
0xfb44│inDamieAt:1 in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
void StkCntxt_returnFromTaskSub( struct StStackAndContext** cppStAg)
│ │
├───────┤
│pStPcContextAt│←┐hl,sp at PcCntxt_suspend(.)
├───────┤
│pStStateDelayAt
├───────┤
0xfb34│ppStDelayPcContextAg │0xfb3e
├───────┤
│HL │ │0xfb3a
├───────┤
0xfb38│return address of PcCntxt_suspend(.) │0x083f
├───────┤
0xfb3a│chAt │ ←┐hl,sp │0x00: 1 byte
├───────┤ │ │
│inAt │ │
├───────┤ │ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0x0084
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x0668
├───────┤ │を呼び出すことで作られ
0xfb44│inDamieAt:1 in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
start task and suspend 動作の一例( 2 個の auto variable)
│ │
├───────┤
│pStPcContextAt│←┐hl,sp at PcCntxt_suspend(.)
├───────┤
│pStStateDelayAt
├───────┤
0xfb34│ppStDelayPcContextAg │0xfb3e
├───────┤
│HL │ │0xfb3a
├───────┤
0xfb38│return address of PcCntxt_suspend(.) │0x083f
├───────┤
0xfb39│chAt │ ←┐hl,sp │0x00: 1 byte
├───────┤ │ │
│inAt │ │
├───────┤ │ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0x0084
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x0668
├───────┤ │を呼び出すことで作られ
│inDamieAt in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
PcCntxt_return2Task 動作の一例(testTask に戻る)
│ │
├───────┤
│pStPcContextAt│←┐hl,sp at PcCntxt_suspend(.)
├───────┤
│pStStateDelayAt
├───────┤
0xfb32│ppStDelayPcContextAg │0xfb3e
├───────┤
│HL │ │0xfb3a
├───────┤
0xfb36│return address of PcCntxt_suspend(.) │0x083f
├───────┤
0xfb38│byReturnValueAt ←┐hl,sp
├───────┤ │
│pStPcContextAt │
├───────┤ │
│pStStateDelayAt │
├───────┤ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0x0084
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x067A
├───────┤ │を呼び出すことで作られ
0xfb44│inDamieAt in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
PcCntxt_return2Task 動作の一例(testTask に戻る) at PcCntxt_executeLoopPolling(.)
│ │
├───────┤
0xfb40│inReturnValueAt ←┐hl,sp
├───────┤ │
│pStPcContextAt │
├───────┤ │
│pStStateDelayAt │
├───────┤ │
fb3e●│pStDelayPcContextAg↓of PcCntxt_return2Task(.) HLref2 │0xfb74
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb4c
├───────┤ │
0xfb4a│PcCntxt_return2Task(.) への return address │0x06f5
├───────┤ │を呼び出すことで作られ
0xfb4c│inDamieAt in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
PcCntxt_call 動作の一例
│ │
├───────┤
0xfb34│ppStDelayPcContextAg │0xfb3e
├───────┤
│HL │ │0xfb3a
├───────┤
0xfb38│return address of PcCntxt_call(&pStDelayPcContextAg) │0x0a06
├───────┤
0xfb3a│chAt
├───────┤
│inAt
├───────┤
fb3e●│pStDelayPcContext Ag of PcCntxt_return2Task(.) HLref2 │0xfb74 → de = pStDelayPcContext
├───────┤ │
│hl of PcCntxt_return2Task をここに移動する。== HLret2 │0xfb44 → 関数address ←→ hl value, sp
├───────┤ │
0xfb42│PcCntxt_return2Task(.) への return address │0x067A
├───────┤ │を呼び出すことで作られ
0xfb44│inDamieAt in PcCntxt_executeLoopPolling(.) │る Stack データ構造。
├───────┤ │
│pStStateDelayAt in PcCntxt_executeLoopPolling(.) │
├───────┤ │
│return address│ │
├───────┤ │
│ │
IntCntxt_suspend 動作の一例(testTsk2 に戻る)
0xfb2c│ppStDelayPcContextAg of PcCntxt_suspend(.) │0xfb32
├───────┤ │
│hl of task をここに移動する。== HLret2 │0xfb32
├───────┤ │
0xfb30│IntCntxt_suspend(ppStPcContestAg) の return address │0x0b7A
├───────┤ │を呼び出すことで作られ
fb32●│pStPcContextAg in PcCntxt_startImmediately(.) │0fbae
├───────┤ │
0xfb34│HL │ │0b38
├───────┤ │
0xfb36│return address│of task(pStPcContextAt, inAg.) │0706
├───────┤ │
0xfb3e│ │ │
├───────┤ │
│ │
IntCntxt_return2Task 動作の一例(testTask に戻る)
│task 側への戻りアドレス・飛び先 │0x0b8c
├───────┤ │を呼び出すことで作られ
fb34●│pStStateDelayAg in IntCntxt_return2Task(pStPcContextAg│0fbae
├───────┤ │
0xfb36│HL │ │0xfb3a
├───────┤ │
0xfb38│return address│of IntCntxt_return2Task(pStPcContextAg)│ 0x91c
├───────┤ │
0xfb3a│inDamieAt in PcCntxt_executeLoopPolling(.) │0x0009
├───────┤ │
│ │
ドキュメント・メモ
1h 78K0 context switch
IntCntxt
e←-- IntOOP.h を include するだけで使えるはず
e←-- PcCntxt_executeLoopPolling() と task polling() を同じ優先度で実行すべき
ではない。main loop の最後におくべきである。
←-- CntxtGrp_lastPolling() を追加する