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環境の最新インストールパッケージとドキュメントを無償ダウンロードすることができます。

MAXQ20コアのハードウェアスタック操作

MAXQ20コアでは、次の2種類のスタック操作が使用されています。

  • PUSH操作(オペコードPUSH、LCALL、およびSCALLを含む)は、スタックにデータを格納するために使用されます。これらの操作は、スタックポインタSPを1だけプリインクリメントした後、このSPポインタ(@SP)によってポイントされたスタック位置にデータを格納します。
  • POP操作(オペコードPOP、POPI、RET、およびRETIを含む)は、スタックからデータを取得するために使用されます。これらの操作は、SPによってポイントされたスタック位置からデータを取得した後、このスタックポインタを1だけポストインクリメントします。
ハードウェアスタックの主要機能の1つは、サブルーチンの呼び出し時のアドレスの保存と復元であるため、スタックは16ビット(ワード)ロケーションで構成されます。この幅によって、単一のプッシュまたはポップ操作で、16ビットInstruction Pointer (IP)レジスタを保存または復元することができます。PUSHまたはPOPがスタックに対して8ビットレジスタ(APなど)の保存または復元に使用される場合でも、各スタック操作で16ビットワード全体が常に使用されます。

スタックを利用するさまざまなオペコードのほかに、次の2つの環境でも、マイクロコントローラはハードウェアスタックを自動的に使用します。

  • 割込みサービスが提供された場合、現在のプログラム実行ポイントは、割込みサービスルーチン(割り込みベクトルレジスタIVによってポイントされる)の実行が開始されるまでスタックにプッシュされます。
  • Utility ROM内の実行するコードの実行が必要な特定のデバッグコマンド(Read RegisterやWrite Data Memoryなど)が呼び出された場合、現在の実行ポイントは、制御がUtility ROMに転送されるまで、スタックにプッシュされます。Utility ROMのデバッグルーチンが完了すると、実行ポイントは、スタックをポップオフし、プロセッサの前の状態が復元されます。
割込みサービスルーチンにベクトル指定するか、デバッガコマンドを実行する場合、常に、ハードウェアスタックの使用が必要になります。この動作はハードウェアに埋め込まれているため、それを変更する方法はありません。しかし、より一般的なPUSH/POPやCALL/RET命令ペアの場合、ソフトスタックを代わりに実装することができます。

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ベースマイクロコントローラのデータメモリにリプレースメントフトスタックを簡単に実装することができます。このソフトスタックは、サブルーチンをさらにモジュラー化し再使用可能なものにすることによって、大規模なアセンブリベースアプリケーションの開発を支援します。また、スタックによって、スタックベースのエラーの検出も可能です。