MAXQ2000のデータメモリへのソフトスタックの実装
要約
このアプリケーションノートでは、アセンブリベースアプリケーション用のデータメモリにソフトスタックを実装する簡単な方法について説明します。この方法は、MAXQ2000、およびその他のMAXQ20ベースマイクロコントローラを使用します。サンプルコードは、マキシムのMAXQ®ファミリ用プロジェクトベースアプリケーション開発/デバッグ環境である、MAX-IDEのマクロプリプロセス機能を使用して記述されています。
Overview
MAXQ2000マイクロコントローラは、マキシムのRISCマイクロコントローラファミリの他のMAXQデバイスと同様に、MAXQ20コアをベースにしています。MAXQ20ベースマイクロコントローラは通常、データ/コード領域とは別個の専用内部メモリに格納された、固定数のレベル(MAXQ2000では16)を持つ16ビット幅のハードウェアスタックを実装しています。このハードウェアスタックは、サブルーチンコールおよび割込み操作におけるマイクロコントローラの動作状態の保存と復元を行うために使用されます。
小規模の厳密に絞り込まれたアプリケーションに最適ですが、ハードウェアスタックは、深くネストしたサブルーチン(またはスタック上の少数の作業レジスタよりも数多く保存と復元を行うサブルーチン)が大規模なアセンブリアプリケーションで使用された場合、たちまちスペース不足になります。Cプログラミング言語(IARのEmbedded Workbench®などのコンパイラを使用)で記述されたアプリケーションまたはRowley Associates社のCrossworks for MAXQは、データメモリに含まれる「ソフトスタック」を利用することによって、この問題を回避します。このソフトスタックは、サブルーチンのコール/リターンアドレスやローカル作業変数を格納します。しかし、MAXQ20コアには、アセンブリ専用アプリケーションで使用可能なデータメモリ内のスタックの場所を見つけるための内部機構がありません。
このアプリケーションノートでは、アセンブリベースアプリケーション用のソフトスタックをデータメモリに実装する簡単な方法について説明します。このアプリケーションノートで提示されるコードは、MAXQ2000や他のMAXQ20ベースマイクロコントローラ上で使用することができます。サンプルコードは、マキシムのMAXQファミリ用プロジェクトベースアプリケーション開発/デバッグ環境である、MAX-IDEのマクロプリプロセス機能を使用して記述されています。
以下のMAX-IDE環境の最新インストールパッケージとドキュメントを無償ダウンロードすることができます。
- MAX-IDE Installation (ZIP)
- MAXQ Core Assembly Guide (PDF)
- Development Tools Guide (PDF)
MAXQ20コアのハードウェアスタック操作
MAXQ20コアでは、次の2種類のスタック操作が使用されています。
- PUSH操作(オペコードPUSH、LCALL、およびSCALLを含む)は、スタックにデータを格納するために使用されます。これらの操作は、スタックポインタSPを1だけプリインクリメントした後、このSPポインタ(@SP)によってポイントされたスタック位置にデータを格納します。
- POP操作(オペコードPOP、POPI、RET、およびRETIを含む)は、スタックからデータを取得するために使用されます。これらの操作は、SPによってポイントされたスタック位置からデータを取得した後、このスタックポインタを1だけポストインクリメントします。
スタックを利用するさまざまなオペコードのほかに、次の2つの環境でも、マイクロコントローラはハードウェアスタックを自動的に使用します。
- 割込みサービスが提供された場合、現在のプログラム実行ポイントは、割込みサービスルーチン(割り込みベクトルレジスタIVによってポイントされる)の実行が開始されるまでスタックにプッシュされます。
- Utility ROM内の実行するコードの実行が必要な特定のデバッグコマンド(Read RegisterやWrite Data Memoryなど)が呼び出された場合、現在の実行ポイントは、制御がUtility ROMに転送されるまで、スタックにプッシュされます。Utility ROMのデバッグルーチンが完了すると、実行ポイントは、スタックをポップオフし、プロセッサの前の状態が復元されます。
MAXQ2000 (MAXQ20コアベースの他のデバイスと同様)は、以下のスタックエラー状態の1つが発生した場合、エラー検出も警告も提供しないことに注意してください。
- スタックオーバフロー:1つの値がすでに満杯のスタックにプッシュされた場合に発生します。このエラーによって、スタック内の最も古い値が上書きされます。
- スタックアンダフロー:スタックが空のときに1つの値がスタックからポップする場合に発生します。このエラーによって、無効なデータ値が返されます。たとえば、スタックが空のときにRETが実行された場合、実行は誤った(潜在的にランダムな)アドレスに転送されます。
データメモリ内のソフトスタックの作成
データメモリ内にソフトスタックを作成する最初のステップは、使用するデータメモリの部分を定義することです。次に、スタックの最上部の現在の位置を追跡するために、データメモリポインタ(DP[0]、DP[1]、またはBP[Offs])を定義する必要があります。注:アプリケーションソフトウェアは他の目的(変数またはバッファなど)のためのスタック専用データメモリを使用しないことに注意してください。
このようなソフトスタックを定義および初期化する簡単な方法には、BP[Offs]レジスタペアと1個の等式が必要です。
SS_BASE equ 0100h ss_init: move DPC, #1Ch ; Set all pointers to word mode move BP, #SS_BASE ; Set base pointer to stack base location move Offs, #0 ; Set stack to start retベースポインタ(BP)レジスタがSS_BASE位置に設定された場合、Offsレジスタをスタックの現在の最上部をポイントするために使用することができます。Offsレジスタがわずか8ビットワイドである場合、ハードウェアはスタックをデータメモリ内のこの範囲(BP .. (BP+255))に制限します。Offsが255に等しいときにプッシュが発生する(オーバフロー)場合、またはOffsが0に等しい場合にポップが発生する(アンダフロー)場合、Offsレジスタは単純にスタックの他端にラップアラウンドします。このアクションは、ハードウェアスタックが操作する方法をシミュレートしており、単純なソフトスタック実装を可能にします。これは、アンダフローおよびオーバフロー状態を検出しません。
ss_initルーチンは、ソフトスタックが使用される前に、メインアプリケーションによって呼び出される必要があります。このルーチンは、BP[Offs]ポインタをワードモードに設定します。これを行うのは、ソフトスタックが16ビットワイドスタックとして実装されるためです。また、このルーチンは、BP[Offs]レジスタペアをスタックの先頭にポイントします。
ソフトスタック操作
スタック(PUSH、POP、CALL、RET、など)を使用する組込みオペコードは、ソフトスタックを使用するために再定義することはできません。これは、各意味がMAXQアセンブラにハードワイヤードされているためです。さらに、割込みサービスルーチンなど、アプリケーションの一部の部分では依然として標準ハードウェアスタックを使用しなければならない場合があります。こうした理由のため、ソフトスタックは標準オペコードを複製するマクロによってアクセスされます。
mpush MACRO Reg move @BP[++Offs], Reg ; Push value to soft stack endm mpop MACRO Reg move Reg, @BP[Offs--] ; Pop value from soft stack endm mcall MACRO Addr LOCAL return move @BP[++Offs], #return ; Push return destination to soft stack jump Addr return: endm mret MACRO jump @BP[Offs--] ; Jump to popped destination from soft stack endm以下では、これらのマクロの操作について検討します。
mpush <reg>
このマクロは、PUSHオペコードと同じ方法で使用されます。このマクロでは、8ビットまたは16ビットレジスタ、またはスタックにプッシュされる即値が許容されます。
mpush A[0] ; Save the value of the A[0] register mpush A[1] ; Save A[1] mpush A[2] ; Save A[2] ... ; code which destroys A[0]-A[2] mpop A[2] ; Restore the value of A[2] (pop in reverse order) mpop A[1] ; Restore A[1] mpop A[0] ; Restore A[0]
mpop <reg>
このマクロは、POPオペコードと同じ方法で使用されます。このマクロによって、上に示したように、8ビットまたは16ビットレジスタをスタックからロードすることができます。16ビット値がプッシュされ、この値が8ビットレジスタにポップされる場合、低バイトのみがレジスタに格納されることに注意してください。高バイトは失われます。これは、組込みハードウェアスタックの動作と同じです。
subroutine: mpush A[0] ; Save the current value of A[0] ... ; Code which destroys A[0] mpop A[1] ; Restore A[0] mret
mcall <address>
mret
mcallマクロは、CALLオペコードと同じ方法でサブルーチンを実行するために使用されます。このサブルーチンは、実行を完了した時点でリターンするためにmretマクロ(標準RETオペコードでない)を使用する必要があります。
mcall mySub ... mySub: mpush A[0] ; Save A[0] mpush A[1] ; Save A[1] ... ; Perform calculations, etc. mpop A[1] mpop A[0] mret
ソフトスタックのサイズの拡大
一部のアプリケーションは、256レベルより大きいソフトスタックを必要とします。他のデータポインタの1つ(DP[0]またはDP[1])を使用することによって、あらゆるサイズ(利用可能なデータメモリの上限値)のスタックを実装することが可能です。
SS_BASE equ 0000h SS_TOP equ 01FFh ss_init: move DPC, #1Ch ; Set all data pointers to word mode move DP[0], #SS_BASE ; Set pointer to stack base location ret上記のコードは、データメモリ内のロケーション0000h~01FFhを予約するため、最大511レベルまで保持可能なスタックを作成します(スタックメモリ領域の1つのロケーションは未使用のままにされます。これによって、mpush/mpop/mcall/mretマクロのより短い、より効率的な実装が可能です)。
単にデータポインタを変更してコード内の他のすべてはそのままにした場合、スタックが拡大されます。しかし、DP[0]がデータメモリの特定範囲に制約されないため、プッシュまたはポップ操作がソフトスタックの指定された境界を越えてDP[0]をインクリメント/デクリメントするのを妨げるものは何もありません。これを回避するために、何か簡単なアンダフロー/オーバフローチェックを追加することができます。
アンダフロー/オーバフローチェックの追加
マクロ(使用時に毎回コード展開)をできる限り短くするために、アンダフローとオーバフローチェックは、ハードウェアスタックを使用するマクロによって呼び出されたサブルーチン内で実行されます。
mpush MACRO Reg call ss_check_over ; Check for possible overflow move @++DP[0], Reg ; Push value to soft stack endm mpop MACRO Reg call ss_check_under ; Check for possible underflow move Reg, @DP[0]-- ; Pop value from soft stack endm mcall MACRO Addr LOCAL return call ss_check_over ; Check for possible overflow move @++DP[0], #return ; Push return destination to soft stack jump Addr return: endm mret MACRO call ss_check_under ; Check for possible underflow jump @DP[0]-- ; Jump to popped destination from soft stack endm ss_check_under: push A[0] push AP push APC push PSF move APC, #80h ; Set Acc to A[0], standard mode, no auto inc/dec move Acc, DP[0] ; Get current value of stack pointer cmp #SS_BASE jump NE, ss_check_under_ok nop ; < Error handler should be implemented here > ss_check_under_ok: pop PSF pop APC pop AP pop A[0] ret ss_check_over: push A[0] push AP push APC push PSF move APC, #80h ; Set Acc to A[0], standard mode, no auto inc/dec move Acc, DP[0] ; Get current value of stack pointer cmp #SS_TOP jump NE, ss_check_over_ok nop ; < Error handler should be implemented here > ss_check_over_ok: pop PSF pop APC pop AP pop A[0] ret上記のコードによって、プッシュまたはポップ(あるいはcallまたはret)操作が発生する前に、現在のスタック位置がチェックされます。オーバフローまたはアンダフローエラーが検出された場合、応答はアプリケーションごとに異なります。通常、この種のエラーは、アプリケーション開発時にのみ発生するものです。コードが正しく記述されている場合は発生しないはずです。エラーが発生した場合、ハードウェアスタックの使用中にアンダフロー/オーバフローが発生した場合と同様に、致命的なエラーと見なされるのが普通です。このエラーに対して可能な応答には、エラーメッセージの停止と送信、またはLEDの点灯が含まれます。開発時に役立つ巧妙な方法は、アンダフローまたはオーバフローが発生した場合に即座にフィードバックを可能にするために、MAX-IDEで、これらの2つの各ルーチン内部にブレークポイント(「Error handler should be implemented here」行など)を設定することです。
しかし、時として、アプリケーションは、(たとえば、最初からアプリケーションサブタスクのリロードと再起動を実行することによって)スタックアンダフローまたはオーバフローから復元することができます。その場合、スタックエラーが発生したことを示すフラグを単純に設定することが望ましい場合があります。このようなフラグの可能な候補には、PSFレジスタに格納される2つの汎用フラグ(GPF0およびGPF1)が含まれます。2つのビットフラグが利用可能であるため、2つの一方はオーバフローを示すために使用し、他方はアンダフローを示すために使用することができます。
結論
MAX-IDEによって提供される強力なマクロプリプロセス機能によって、MAXQ2000およびその他のMAXQ20ベースマイクロコントローラのデータメモリにリプレースメントフトスタックを簡単に実装することができます。このソフトスタックは、サブルーチンをさらにモジュラー化し再使用可能なものにすることによって、大規模なアセンブリベースアプリケーションの開発を支援します。また、スタックによって、スタックベースのエラーの検出も可能です。