通常、私はすべての GPR アクセスの前にバンクセルを実行しますが、多くの不要な行があることに気付きました。それで、質問はこれです。呼び出し元のルーチンとは異なるメモリ バンクにアクセスするサブルーチンをいくつか呼び出します。現在のバンクを維持するために GPR (STATUS レジスタ) に保存し、戻る前にそれに切り替えるのは良い考えですか、それともこれを行う最適な方法は何ですか?
3 に答える
これには多くの方法がありますが、どれだけ賢くなりたいか、どれだけのコード スペースを節約したいか、コードがどれだけタイム クリティカルである必要があるかによって、多くの方法が異なります。コードのスペース/時間を節約しようとすることと、誤って間違ったタイミングで間違った銀行にいるリスクを冒すこととの間にはトレードオフがあります。
ここでは安全なオプションを好みます...
私がしていることは、バンクを変更する可能性のある Sub に対して、GPR ステータスをバックアップする小さな「Calling Sub」を作成することです。次に、「Calling Sub」が「Real Sub」を呼び出します。「Real Sub」は、好きなだけバンクを変更できます。「Real Sub」が終了すると、「Calling Sub」に戻り、プログラムループに戻る前にバンクを元の状態に戻します。
疑似コード:
CALL_MYSUB BACKUP STATUS
BACKUP GPR
CALL MYSUB
RESTORE GPR
RESTORE STATUS
RETURN
上記を巧みに利用して、保存と取得のコードを置き換える小さなマクロを作成できます。これにより、コードの外観が簡素化され、保守性が向上します...もちろん、同じコード空間を使用します。
疑似コード:
CALL_MYSUB BACKUP_MACRO
CALL MYSUB
RESTORE_MACRO
RETURN
理論的には、バックアップする命令と復元する命令が 2 つ以上ある可能性が高い場合は、マクロの代わりに、単一のバックアップ ルーチンと単一の復元ルーチンを作成し、代わりにこれらを呼び出します。
疑似コード:
BACKUP_CONTEXT BACKUP STATUS
BACKUP GPR
BACKUP OTHER THINGS
BACKUP MORE STUFF
RETURN
BACKUP_CONTEXT BACKUP STATUS
BACKUP GPR
BACKUP OTHER THINGS
BACKUP MORE STUFF
RETURN
RESTORE_CONTEXT RESTORE MORE STUFF
RESTORE OTHER THINGS
RESTORE GPR
RESTORE STATUS
RETURN
CALL_MYSUB BACKUP_CONTEXT
CALL MYSUB
RESTORE_CONTEXT
RETURN
もちろん、上記を必要とする各サブに直接組み込むこともできます。ただし、必要に応じてバックアップと復元なしでサブを呼び出すことができ、サブが何をしようとしているのかについてサブを保持し、同様の呼び出しをすべて1か所に配置できるため、上記を好みました。 1 か所で変更を行い、潜水艦が GPR を変更しようとしていることを明らかにします。
ここで注意すべき唯一のことは、入れ子になった Call が多すぎないようにすることです。もちろんスタックをオーバーフローさせてしまいます!
これが役立つことを願っています....
編集:
バンクセレクトビットをバックアップするだけでよい場合は、次の方法をお試しください...
バンク選択ビットのみを保存します。
MOVF STATUS,w ;Get the Current Bank Select bits
ANDLW B'01100000' ;Remove all but the Bank Select bits
MOVWF TEMP1 ;Store in a Temporary Register
バンク選択ビットのみを取得します。
MOVLW B'10011111'
ANDWF STATUS,1 ; Clear the Bank Select Bits
MOVF TEMP1,w ; Get the Old Bank Select Bits
XORWF STATUS,1 ; Restore Old Bank Select Bits
ただし、ここには環境がまったくないため、これが 100% 機能するかどうかは確認できませんが、数学は問題ないと思います!
また、ステータス ビット 6 に RP1、ステータス ビット 5 に RP0 を持つ 16c77 を使用していると仮定しました。
もちろん、IRP ビットも必要な場合は、STAUTS ビット 7 - IRP も保存する必要があります。
また、TEMP1 が GPR に格納されていることを確認して、バンクされないようにする必要があります。
最も簡単なアプローチは、特に指定されていない限り、各ルーチンは RP0 と RP1 が開始時にクリアであると想定することをグローバル ポリシーとして宣言することです。IRP は、FSR に読み込まれるものに応じて適切に設定されます。それ以外の場合は未指定です。終了時には、特に指定されていない限り、すべてのルーチンは RP0 と RP1 をクリアしたままにし、IRP で必要なことを何でも実行する必要があります (ただし、ルーチンが IRP で何も実行しない場合は、その事実を文書化することが役立つ場合があります)。
したがって、バンク 0 に「置かれる」コードは、FSR を使用する場合を除いて、バンク ビットで何もする必要はありません。別のバンクにアクセスするコードは、サブルーチン呼び出しを行う前、または呼び出し元に戻る前に、バンク 0 を再選択する必要があります。
場合によっては、異なる動作を指定するいくつかの特別なメソッドがあると便利な場合がありますが、多くの場合、この「式」は非常に優れているはずです。重要なことは、ほとんどのメソッドが従う規則を持ち、その規則を使用しないメソッドが明確に文書化されていることを明確にすることです。
私は2つのマクロを使用しました:
m_SaveBankマクロ変数
clrf Var
btfsc STATUS,RP1
bsf Var,RP1
btfsc STATUS,RP0
bsf Var,RP0
endm
m_RestoreBankマクロVar
bcf STATUS,RP1
bcf STATUS,RP0
btfsc Var,RP1
bsf STATUS,RP1
btfsc Var,RP0
bsf STATUS,RP0
endm
サブルーチンは、最初にSTATUS.RP1とRP0を保存してから、終了時にそれらを復元する必要があります。ほとんどの場合、サブルーチンのデータを伝送し、RP0とRP1以外のSTATUSビットも影響を受けないため、Wレジスタは変更されません。私はこのように11の言葉を惜しみませんが、他に選択肢はないと思います。