電子工作

PIC18F拡張命令

PIC18Fシリーズの新しいチップでは、拡張命令がサポートされています。コンパイラでコードが最適化されるように考えられたもののようです。

従来の通常の命令セットと拡張命令セットの切り替えは、コンフィグで行うので、実行時の切り替えはできません。

拡張命令は、単に、新しい命令が追加されただけでなく、レジスタをアドレスするほとんどの命令で動作が変わります。言わば、全命令が拡張命令になるようなものです。


RAMアドレッシングの拡張

PIC18Fでの通常のアドレスは、アクセスバンクを用いる方法と、BSR(バンクセレクタ)を用いる方法の二つがあり、これらは、アセンブラ命令語の最後に「ACCESS」をつけると、アクセスバンク。「BANKED」をつけるとBSRアクセスになっていました。このうち、アクセスバンクを用いる部分が拡張されました。

ACCESS / BANKED は書くのが面倒ですが、省略しても適宜アセンブラが判断して解釈してくれるようです。

いずれの方法でも、オペランドが表すアドレスは0x00-0xffの1バイトアドレス。拡張しない場合には、固定のアクセスバンクをアドレスしたのですが、拡張命令では、SFR(機能レジスタ)に対するアクセスは従来通りアクセスし、SFRが割り付けられていないアドレスに対しては、オペランドが、FSR2に対するオフセットとして働きます。

  1. FSRに対するアクセス。

    アセンブラの記法も従来のとおりです。

    	MOVF	PORTA,ACCESS
    

    のように書きます。BSRに関わり無く、いつでもSFRにアクセスできるのは、今までと変わりません。

  2. オフセットつきアクセス

    アセンブラの記法が変わり、次のように書きます。

    	MOVF	[0x20]
    

    「[]」で囲んだ中には、オフセットを書きます。これで、アドレスは、FSR2+0x20を示します。オフセットつき相対アドレスが実現できたわけです。

SFRは、実際には、アクセスバンクの0x60-0xFFに割り当てられています(明示されていませんが、すべてのプロセッサで固定でしょう)。従って、オフセットが利用できる範囲は、0x00-0x5fとなっています。

例えばC言語でのスタックフレームを考えたとき、FSR2をスタックフレームとして利用しようという設計思想のようです。PIC18Fでは、サブルーチンや割り込みの戻りアドレスは、固定のハードウェアスタックにセットされるので、FSR2によるスタックフレームは、引数やローカル変数を指し示すために利用します。FSR2からのオフセットでアドレスを指定できるので、面倒なアドレス計算無しに、引数、ローカル変数をアクセスできる仕掛けが追加されたわけです(96バイトの範囲内ですが…)。

また、命令をフルに利用するには、スタックの構造としては、減少式(アドレスの若い方へスタックが伸びていく)で、FSR2が空のスタック位置(スタック先頭−1)を指すようにした方が便利なようです。

  1. 減少式のスタックを使用する理由は、演算命令で使用する、FSR2のオフセットつき相対アドレスのオフセットが正の方向しか取れないからです。増加式のスタックではスタックフレーム内の変数などは、FSR2より若いアドレスになるので、負のオフセットが要求されるます。これではせっかくのオフセットつき相対アドレスが利用できません。
  2. FSR2が指し示すのは、スタックトップ−1にせざるを得ないのは、PUSH/POP操作を簡単にするためです。まず、定数PUSHに使用するPUSHL命令は、Cで書けば、*(FSR2--)=k。ポストデクリメントになっています。

    また、PUSH操作は、FSR2のアクセス窓を用いて行いますが、アクセス窓機能には、PREDEC2がありません。POSTDEC2を使用することになります。

    	MOVF	POSTDEC2	; *FSR2=WREG then FSR2--
    

    POP操作は、同様にFSR2のアクセス窓のPREINC2を使います。

    	MOVWF	PREINC2	; FSR2++ then WREG=*FSR2
    

    もちろん、PUSH / POP 操作で、自前でFSR2を操作することにすれば、FSR2がスタックトップを示す形式も可能ですが、コードが増えます。

    ;	PUSH
    ;
    	SUBFSR	1		; FSR2--;
    	MOVWF	INDF2	; *FSR2=WREG
    ;
    ;	POP
    ;
    	MOVWF	POSTINC2	; WREG=*FSR2 then FSR2++
    ;
    

追加された命令

新たに、拡張命令として追加された命令は、ADDFSR / ADDULNK / CALLW / MOVSF / MOVSS / PUSHL / SUBFSR / SUBULNK のわずか8命令。主に、スタックフレームの操作に関する命令です。

  1. ENTER / LEAVE機能

    サブルーチン内のスタックフレームを生成する(ENTER)には、スタック・フレームのサイズに応じてスタック・フレーム・ポインタ(=FSR2)を増減する必要があります。

    減少式のスタック(アドレスの若い方へスタックが伸びていく)ばあいには、SUBFSR が使用できます。SUBFSRFSR2から定数を減算するので、その定数分のスタックフレームが確保されます。

    逆に、サブルーチンから戻る場合(LEAVE)には、放棄するバイト数分だけFSR2に加算しなければなりません。ADDULNK は、定数をFSR2に加算し、さらに、RETURN 機能を兼ねるというお得な命令です。

    従って、減少式のスタックの場合には、SUBFSR / ADDULNK の組合せで簡単にサブルーチン内のスタックフレーム生成/放棄ができます。

    同様に、増加式のスタックの場合には、ADDFSR / SUBULNK の組合せを用いれば良いわけです。通常は、減少式のスタックを用いた方が簡単でしょう。

  2. スタックフレームのデータ移動

    レジスタ操作で最も多いのは単なる複写。PIC18FではMOVFFという1命令でバンクを超えて、しかも、WREGを壊さずに複写できる命令があります。しかし、これをスタックフレーム内の複写に使用するには、アドレス計算をしなければならず、煩雑です。この命令のスタックフレーム内でのサポート命令が、MOVSS 命令。次のように書きます。

    	MOVSS	[0x10],[0x20]
    

    これで、FSR2+0x10の内容をFSR2+0x20へ複写します。スタックフレーム内でお手軽にデータ複写ができるわけです。

    さらに、スタックフレームから、指定アドレスへの複写もサポートされています。MOVSF命令です。

    	MOVSF	[0x10],DESTNAME
    

    バンクを意識せずに一命令でスタックフレームから、任意のアドレスに複写ができます。逆向きもあれば、もっと、便利だったのでしょうけれど…

  3. 定数プッシュ

    PUSHL は、FSR2で示されるスタックに定数をPUSHします。このとき、FSR2が減少するので、この命令は減少式のスタックを想定していることになります。WREGを経由せずに定数をPUSHできるところが、便利な点です。

  4. 間接CALL

    PIC18Fの間接呼び出しは、ちょっと、面倒。アドレスは3バイトあるので、基本的には、PCLATU / PCLATH に事前にセットして置き、最後に PCL に書き込みをするとそこへ、ジャンプします。あくまでジャンプなので、一旦、CALLしてその中から間接ジャンプする必要がありました。

    CALLWは、その不便を多少、解消するもの。WREGの値を元にその場で、間接CALLを行ってくれます。分岐先は、PCLATU / PCLATH / WREG なので、実際には、分岐先は256バイトの範囲の制限があるし、普通には、256バイトバウンダリーのテーブルの方が楽になるという制限つきなのですが…

    実は、このCALLWそれほど、便利かというと、それほどでもありません。PCLATU / PCLATH のセットを事前にする必要があります。ところが、PIC18Fでは、PCLを読むとPCLATU / PCLATH に 実際のPCの該当値をセットする機能があるので、これを利用して簡単にテーブル呼び出しを実現する例がマニュアルに書かれています。

    		MOVF OFFSET, W
    		CALL TABLE
    		.
    		.
    		ORG		nn00h
    TABLE		ADDWF	PCL
    		RETLW	nnh
    		RETLW	nnh
    		RETLW	nnh
    		.
    		.
    

    ミソは、ADDWF PCL。PCLに加算するということは、PCLを一度読んでから、加算をしてPCLに書き込んでいます。この動作を、細かく見ていくと、PCLから読んだ時点で、PCLATU / PCLATH には、現在のPCの該当バイトがセットされます。PCLに書き込んだ時点で間接ジャンプします。このお陰で、PCLATU / PCLATH をセットするコードを書かなくても済むので、単純なテーブル引用ならば、この書き方のほうがCALLWを使うよりも楽な気がします。

    本格的な計算を行う間接コールならば、余計なCALLスタブがいらない分だけ、便利になっています。


ちょっとした考察

アセンブラで拡張命令を使う場合、ポイントになるのは、FSR2の扱いです。FSR2をスタック・フレーム・ポインタとして使うならば、サブルーチン内に用意にスタック・フレームを設けられるので、簡単に、ローカル変数を使用できます。これは、プログラムを作るときに、とても、便利な機能です。

他方で、スタック・フレームポインタとして使わなければ、オフセット付きのインデックス・アクセスができるので、構造体などを扱う場合には、とても、楽になります。構造体のメンバーがポインターで与えられたとき、メンバーのアドレスを計算しなくても直接アクセスできるのは、こちらも、便利な機能。メンバーを読み取るだけなら、FSRのアクセス窓PLUSWを使えばできるのですが、これは、書き込みは無理だし、WREGを使用してしまいます。

ADDFSR / SUBFSR は、別の見方をすると、2バイト±1バイトの定数加減算機能。オフセット計算などが1命令でできるので、便利な命令です。対象が、FSR2に限られているのでそこが難点。

メニューへ戻る
アルゴ算法堂へ戻る