第 4 回 アセンブラ演習

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

4-1. 演習問題

例4-1

あらかじめ与えられた特定のパターン(例えば 2 進数で与える)の値を PORTB に出力し、 LED を光らせたままにするプログラムを作りなさい。 (ヒント: 実行しているプログラムの番地はアセンブリ言語では $ で表します。 プログラムを止めるには sleep ではなく、単なる無限ループを作ります。)

解答例

;************
;*  例4-1 *
;************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB

	movlw   b'01010101' ; 点灯させるビットパターン
	movwf   PORTB
        goto    $
	end

例4-2

あらかじめ与えられたふたつの特定のパターン(例えば 2 進数で与える)の値 のうちのどちらかが on になっている部分の LED を光らせるプログラムを作 りなさい。 (ヒント: ior 命令)

解答例

どちらのビットが on の時、on にするには両方のビットパターンに対して (inclusibe) OR を行います。 x or 0 = x, x or 1 = 1 より、ビットパターンが 0 の部分はそのままの値に なり、ビットパターンが 1 の部分は 1 になります。


;************
;*  例4-2 *
;************
        list p=16f628a
	#include p16f628a.inc

	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB

	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	iorwf   PORTB,1
        goto    $
	end

演習4-1

あらかじめ与えられたふたつの特定のパターン(例えば 2 進数で与える)の値 のうちの両方が on になっている部分の LED を光らせるプログラムを作 りなさい。 (ヒント: and 命令)

演習4-2

あらかじめ与えられた特定のパターン(例えば 2 進数で与える)の値に対して、 2 進数で指定される特定の部分だけ反転させて LED を光らせるプログラムを作 りなさい。 (ヒント: xor 命令)

演習4-3

PORTB に接続した LED になにかパターンを表示し、 RA5 に接続した sw を押 した時に On , Off が反転するプログラムを作りなさい。

演習4-4

PORTB に接続した LED が RA5 に接続した sw を押す度に On , Off が反転す るプログラムを作りなさい。

例4-3

LED のつながっている PORT B に対して、 PB0 のみ、 PB0 と PB1、 PB0 から PB2 まで、と順番に一つずつビットを 1 にしていき、全て 1 になったら全て消灯するプログラムを作りなさい。

解答例

第 2 回の flash プログラムを流用 します。

各パターンを書き込んで、wait を呼び出します。


;************
;*  例4-3 *
;************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	#define	time D'3'
	cblock 0x20
	wa0,wa1,wa2
	endc

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
        movlw   b'00000000'
	movwf	PORTB
	call    wait
        movlw   b'00000001'
	movwf	PORTB
	call    wait
        movlw   b'00000011'
	movwf	PORTB
	call    wait
        movlw   b'00000111'
	movwf	PORTB
	call    wait
        movlw   b'00001111'
	movwf	PORTB
	call    wait
        movlw   b'00011111'
	movwf	PORTB
	call    wait
        movlw   b'00111111'
	movwf	PORTB
	call    wait
        movlw   b'01111111'
	movwf	PORTB
	call    wait
        movlw   b'11111111'
	movwf	PORTB
	call    wait
        goto    main

wait	
	movlw	time
	movwf	wa0
wait0
	clrf    wa1
wait1
	clrf	wa2
wait2
		nop
	decfsz	wa2,1
	goto	wait2
	decfsz  wa1,1
	goto    wait1
	decfsz  wa0,1
	goto    wait0
	return
	end
解答例 2: マクロを使う

これで美しくないと思った場合の別解を示します。 まず、考えられるのは似たようなパターンが出てきますので、これを一つのマ クロ命令で置き換えることです。


;**************
;*  例4-3 2 *
;**************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	#define	time D'3'
	cblock 0x20
	wa0,wa1,wa2
	endc

display macro pattern
        movlw   pattern
	movwf	PORTB
	call    wait
	endm

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
        display b'00000000'
        display b'00000001'
        display b'00000011'
        display b'00000111'
        display b'00001111'
        display b'00011111'
        display b'00111111'
        display b'01111111'
        display b'11111111'
        goto    main

wait	
	movlw	time
	movwf	wa0
wait0
	clrf    wa1
wait1
	clrf	wa2
wait2
		nop
	decfsz	wa2,1
	goto	wait2
	decfsz  wa1,1
	goto    wait1
	decfsz  wa0,1
	goto    wait0
	return
	end
解答例 3: ループを使い、表示部分を最小にする

プログラミングのコツとして、入出力部分を一箇所にまとめて、最小限にする ことが考えられます。 ループ変数を用意して、出力パターンを計算して出力することを考えます。 C 言語で書くと次のようになります。


for(;;){
  pattern=0;
  for(counter=1; counter<256; counter*=2){
    pattern = counter | i;
    pattern の出力;
  }
}

counter *= 2 を実現するには rotateあるいは shift と呼ばれる命令を使います。 これはレジスタの内容をビット毎に右又は左に移動する命令です。 単なる移動は shift で、溢れた bit を反対側の bit に回すのが rotate です。 PIC では STATUS の C bit を含めた rotate 命令のみがあります。 bit 毎に移動するということは、二進数で桁をずらすことになるので、 2 倍、 あるいは 1/2 にするということになります。 つまり、値を 2 倍するには左に rotate すれば良いことになります。 また、rotate した値が256 を越えると STATUS レジスタの C が 1 になりま す。 なお、C 言語にも shift 演算 << と >> があります。 例えば、 count*=2(2倍する) は count<<=1(左に 1 bit シフトする) と書けます。

但し、 counter に 1 が入ってから、 rotate で C=1 となる まで 8 回の繰り返しになるので、このプログラムのままでは全消灯を含めた 9 パターンは表示できません。


;**************
;*  例4-3 3 *
;**************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	#define	time D'3'
	cblock 0x20
	counter
	pattern
	wa0,wa1,wa2
	endc


	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
        clrf    pattern
        movlw   1
	movwf   counter
loop
        movf    counter,0
        iorwf    pattern,1
        movf    pattern,0
        movwf   PORTB    ; 出力は一箇所
        call    wait
	bcf     STATUS,C
        rlf     counter,1
	btfss	STATUS,C
	goto    loop
	goto	main

wait	
	movlw	time
	movwf	wa0
wait0
	clrf    wa1
wait1
	clrf	wa2
wait2
		nop
	decfsz	wa2,1
	goto	wait2
	decfsz  wa1,1
	goto    wait1
	decfsz  wa0,1
	goto    wait0
	return
	end
解答例 4: パターンをパラメータで呼び出す

前の解法はエレガントですが、パターンを計算して出力する点で応用が効きま せん。 そこで、パターンはデータとして列挙し、パラメータで呼び出すことにします。 そして、出力はやはり一箇所で行うようにします。

そのために使うのが算術ジャンプです。 パターンを RETLW で列挙して、W レジスタの値に応じてパターンを受けとり ます。

パターンの最大数はパターンの終了アドレスからパターンの開始アドレスを引 いて求めると、あとでパターンの変更をしてもパターンを出力するプログラム を変更する必要が無くなります。


;**************
;*  例4-3 4 *
;**************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	#define	time D'3'
	cblock 0x20
	counter
	wa0,wa1,wa2
	endc

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
        clrf   counter
loop
        movf    counter,0
	call	getpat
        movwf   PORTB    ; 出力は一箇所
        call    wait
	incf    counter,1
	movlw   enddata-startdata
	subwf   counter,0
	btfss   STATUS,Z
	goto    loop
        goto    main


getpat	
        addwf   PCL,1
startdata
	retlw   b'00000000'
	retlw   b'00000001'
	retlw   b'00000011'
	retlw   b'00000111'
	retlw   b'00001111'
	retlw   b'00011111'
	retlw   b'00111111'
	retlw   b'01111111'
	retlw   b'11111111'
enddata

wait	
	movlw	time
	movwf	wa0
wait0
	clrf    wa1
wait1
	clrf	wa2
wait2
		nop
	decfsz	wa2,1
	goto	wait2
	decfsz  wa1,1
	goto    wait1
	decfsz  wa0,1
	goto    wait0
	return
	end

演習4-5

PORTB につながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを作りなさい。

演習4-6

PORTB に接続した LED が RA5 に接続した sw を押す度に PORT B に つながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを作りなさい。

4-2. 解答と解説

演習4-1

ビットを off にするには off にしたいビットパターンを作り、AND を行います。 x and 0 = 0, x and 1 = x より、ビットパターンが 0 の部分は 0 になり、 ビットパターンが 1 の部分はそのままの値になります。


;************
;*  演習4-1 *
;************
        list p=16f628a
	#include p16f628a.inc

	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	andwf   PORTB,1
        goto    $
	end

演習4-2

ビットを反転させるには反転させたいビットを指定するビットパターンを作り、 XOR を行います。 x xor 0 = x, x xor 1 = x より、ビットパ ターンが 0 の部分はそのままになり、 ビットパターンが 1 の部分は値が反転します。


;************
;*  演習4-2 *
;************
        list p=16f628a
	#include p16f628a.inc

	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	xorwf   PORTB,1
        goto    $
	end

演習4-3

押してない時と押した時で PORTB の値を変化させます。 あらかじめ ON の時のパターンを W レジスタに入れておきます。 その後、btsfc PORTA,5 で RA5 のビットを検査します。 もし、OFF だったら押してないパターンを W レジスタに入れます。 そして、W レジスタの内容を PORTB に入れます。

そのため、 ON の時のパターン、 OFF の時のパターンをあらかじめ作ってお きます。 #define ディレクティヴで bitpattern ラベルにパターンを割り当てておきます。 ON の時のパターンはそのまま、OFF の時のパターンは b'11111111' と XOR を取って反転させておきます。

また、あらかじめ PORTA のコンパレータを無効にした後、 TRISA に b'00100000' を入れて、RA5 を入力ポートに割り当てておきます。


;************
;*  演習4-3 *
;************
        list p=16f628a
	#include p16f628a.inc
	cblock 0x20
	 onpattern
	 offpattern
	endc
	#define bitpattern b'00011111'
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	movlw	0x07
	movwf	CMCON
	banksel TRISA
	movlw   b'0010000'
	movwf   TRISA
	clrf    TRISB
	banksel onpattern
	clrf    PORTA
	movlw   bitpattern
        movwf   onpattern
	xorlw	b'11111111'
	movwf	offpattern
main
	movf    offpattern,0
	btfsc	PORTA,5
	movf    onpattern,0
	movwf	PORTB
        goto    main
	end

演習4-4

演習4-3 と同様の準備が必要です。

sw が押される度というのはどうやって考えれば良いでしょうか? sw は押している間 1 で、離すと 0 になります。 「1 なら反転」とすると、押している間反転を繰り返してしまうことになりま す。

押す度に切替えるには、押した瞬間に切替え、その後、幾ら押し続けても反転 しないようにする必要があります。 つまり、sw が押されてない状態から押された状態への変化を捉え、そこで反 転させます。 そのためには、直前の動作をファイルレジスタに取っておき、直前が 0 でス イッチが 1 の時だけ反転するようにします。


;************
;*  演習4-4 *
;************
        list p=16f628a
	#include p16f628a.inc
	cblock 0x20
	 prev
	endc
	#define bitpattern b'00011111'
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	org	0x0000
	goto	start
	org	0x0008
start
	movlw	0x07
	movwf	CMCON
	banksel TRISA
	movlw   b'0010000'
	movwf   TRISA
	clrf    TRISB
	banksel PORTB
	movlw   bitpattern
        movwf   PORTB
	clrf    prev
main
	btfsc	PORTA,5
	goto    swon
swoff
        clrf    prev
	goto    main
swon
        movf    prev,1
        btfss   STATUS,Z
	goto    main
	movlw	d'1'
	movwf	prev
	movlw	b'11111111'
	xorwf	PORTB,1
        goto    main
	end

なお、このように入力に対して動作が変化するようなプログラムをデバッグす る時は、 Stimulus を使用します(MPLAB IDE User's Guide 14 章 p.147)。 今回は手入力で RA5 を On, Off させますので、 CPU クロックではなく手の 入力に同期してシミュレーション中に RA5 を On, Off させる必要があります。

  1. Debugger → Stimulus Contoroler → New Senario を選びます
  2. Ansynchronous Stimulus の欄で、 「Pin/SFR の欄に RA5、Action に Set High」 を入れた行と 「Pin/SFR の欄に RA5、Action に Set Low」 を入れた行を用意します
  3. Simulator を Animate で走らせます
  4. Animate 中に適宜 Fire 欄をクリックします。クリックすると RA5 が High や Low に切り替わり、プログラムの動きが変化します

演習4-5

例4-3 4 のプログラムにおいて出力パターンを変更したものを作ります。 出力パターンは例4-3 のプログラムを動かして、どの LED がどのビットに 対応しているのかを調べると簡単です。


;************
;*  演習4-5 *
;************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	#define	time D'3'
	cblock 0x20
	counter
	wa0,wa1,wa2
	endc

	org	0x0000
	goto	start
	org	0x0008
start
	banksel TRISB
	clrf    TRISB
	banksel PORTB
main
        clrf   counter
loop
        movf    counter,0
	call	getpat
        movwf   PORTB    ; 出力は一箇所
        call    wait
	incf    counter,1
	movlw   enddata-startdata
	subwf   counter,0
	btfss   STATUS,Z
	goto    loop
        goto    main


getpat	
        addwf   PCL,1
startdata
	retlw   b'11111011' ; 0. このパターンは配線によって変わります
	retlw   b'10000010' ; 1
	retlw   b'11011101' ; 2.
	retlw   b'11001110' ; 3
	retlw   b'10100111' ; 4.
	retlw   b'01101110' ; 5
	retlw   b'01111111' ; 6.
	retlw   b'11100010' ; 7
	retlw   b'11111111' ; 8.
	retlw   b'11101110' ; 9
	retlw   b'11110111' ; A.
	retlw   b'00111110' ; b
	retlw   b'01111001' ; C.
	retlw   b'10011110' ; d
	retlw   b'01111101' ; E.
	retlw   b'01110100' ; F
enddata

wait	
	movlw	time
	movwf	wa0
wait0
	clrf    wa1
wait1
	clrf	wa2
wait2
		nop
	decfsz	wa2,1
	goto	wait2
	decfsz  wa1,1
	goto    wait1
	decfsz  wa0,1
	goto    wait0
	return
	end

演習4-6

演習4-5 のプログラムと 演習4-4 のプログラムを組み合わせます。 counter という変数を用意し、次のサブルーチンを用意します。

  1. counter の値を表示するサブルーチン
  2. counter を 1 増やし、 counter の値を表示するサブルーチン。なお、こ れは 1 を増やすサブルーチンを書き、その直後に値を表示するサブルーチン を書くことで実現できます。

このような準備をした後、次のプログラムを作ります。

  1. counter を 0 にし、counter の値を表示します
  2. RA5 の sw が押される瞬間を監視します
  3. sw がおされると counter を 1 増やし、 counter を表示します
  4. 2 に戻ります。

プログラムは次のようになります


;************
;*  演習4-6 *
;************
        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env

	cblock 0x20
	 prev
	 counter
	endc

	org	0x0000
	goto	start
	org	0x0008
start
	movlw	0x07
	movwf	CMCON
	banksel TRISA
	movlw   b'0010000'
	movwf   TRISA
	clrf    TRISB
	banksel PORTA
	clrf    PORTA
	clrf    prev
	clrf	counter
	call	dispcount
main
	btfsc	PORTA,5
	goto    swon
swoff
        clrf    prev
	goto    main
swon
        movf    prev,1
        btfss   STATUS,Z
	goto    main
	movlw	d'1'
	movwf	prev

	call	inccount
        goto    main

inccount
	incf    counter,1
	movlw   enddata-startdata
	subwf   counter,0
	btfsc   STATUS,Z
        clrf    counter

dispcount
        movf    counter,0
	call	getpat
	movwf	PORTB
	return

getpat	
        addwf   PCL,1
startdata
	retlw   b'11111011' ; 0. このパターンは配線によって変わります
	retlw   b'10000010' ; 1
	retlw   b'11011101' ; 2.
	retlw   b'11001110' ; 3
	retlw   b'10100111' ; 4.
	retlw   b'01101110' ; 5
	retlw   b'01111111' ; 6.
	retlw   b'11100010' ; 7
	retlw   b'11111111' ; 8.
	retlw   b'11101110' ; 9
	retlw   b'11110111' ; A.
	retlw   b'00111110' ; b
	retlw   b'01111001' ; C.
	retlw   b'10011110' ; d
	retlw   b'01111101' ; E.
	retlw   b'01110100' ; F
enddata

	end

4-3. マクロを使って部品化する

初期化

プログラムの初期化の部分は全て同じなので、 ファイルに書き込んでおくと、プログラムから #include ディレクティヴで呼び 出すことで初期化が出来るようになります。 書き込む部分は、具体的には毎回同じである下記の部分です。


        list p=16f628a
	#include p16f628a.inc
	variable env
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
	__config env
	cblock  0x20
	endc

なお、前回はコンパレータも OFF にするように初期化しましたが、 RA0-3 までを出力に使う分にはその初期化は不要なので、このマクロには入れ ていません。

マクロ定義

幾つかプログラムを作って来ましたが、各プログラムを書く際に、毎回、共通 部分があることに気付くと思います。 そこで、 macro 機能を使って各共通部分を意味のある部分毎にまとめて行き たいと思います。

課題を解決するため、 プログラムに機能を付け足すことを考えます。 基本的にはその機能は、実際の機能部分と、それの機能を呼び出す前にしてお く準備部分が存在します。 つまり、付け足すプログラムは、初期化部分と実際のプログラム部分の二ヶ所 となるパターンが一般的です。 そこで、プログラムに付け足す機能を xxx とする時、マクロとして初期化の 部分を xxx_init とし、実際の機能部分を xxx_prg という二つを用意するこ とにします。

まとめたマクロの定義は初期化のファイルに入れることにします。 但し、 nolist と list ディレクティヴではさみ、アセンブル時にリストに定 義部分が出ないようにします。 (マクロを使用すると、使用した部分が展開されてリストに現れますので問題 ありません) 初期化のファイルの名前を gs2.inc とし、#include "gs2.inc" と呼び出すことで初期化とマクロ定義をすることにします。 初期化部分で、必要なファイルレジスタの確保には cblock ディレクティブを 使用します。 ファイルレジスタのアドレスは固定したくないので、 cblock を使う際、引 数にファイルレジスタ上の開始番地を指定しないことにします。 開始番地を省略すると、前回の割当の次の番地から割り当てられます。 したがって、最初の初期化の部分でに空のcblock 0x20 を宣言 すれば、各 xxx_init マクロで引数無しの cblock が使え、 0x20 番地以降に 順番に割り当てられます。

ポート初期化マクロ

PORTB のみ、または PORTA と PORTB をできるだけ出力ポートとして使 う初期化を enable_PB と enable_PAPB として定義します。 PORTA を使う際、コンパレータを無効にする必要があります。


enable_PB macro
	banksel TRISB
	clrf    TRISB
	banksel PORTB
	clrf    PORTB
	endm
enable_PAPB macro
	movlw	0x07
	movwf	CMCON
	banksel TRISA
	movlw   b'0010000'
	movwf   TRISA
	clrf    TRISB
	banksel PORTA
	clrf    PORTA
	clrf	PORTB
	endm

sw を押す度にサブルーチンを呼ぶマクロ

演習4-4 にある sw を押す度になにかをするマクロを作ります。 呼び出し名を sw とし、呼び出し先を func とすると、次のようになります。


sw_init macro
        cblock
	sw_prev
	endc
	clrf    sw_prev
	endm
sw_prg	macro func
sw_main
	btfsc	PORTA,5
	goto    sw_on
sw_off
        clrf    sw_prev
	goto    sw_main
sw_on
        movf    sw_prev,1
        btfss   STATUS,Z
	goto    sw_main
	movlw	d'1'
	movwf	sw_prev
	call    func
        goto    sw_main
	endm

ビジーウェイト

プログラム中で単純なループにより時間待ちをさせることを ビジーウェイトと呼びます。 これは機械語プログラムでは基本的なテクニックですが、他のプログラムとの 協調が絡むと使えなかったりますので利用には注意が必要です。 呼び出し名を bwait とし、繰り返しのパ ラメータ bwait_time を初期値で与えられるようにします。


bwait_init macro _time
	#define	bwait_time _time
	cblock
	bwait_v0,bwait_v1,bwait_v2
	endc
	endm

bwait_prg macro
bwait	
	movlw	bwait_time
	movwf	bwait_v0
bwait0
	clrf    bwait_v1
bwait1
	clrf	bwait_v2
bwait2
		nop
	decfsz	bwait_v2,1
	goto	bwait2
	decfsz  bwait_v1,1
	goto    bwait1
	decfsz  bwait_v0,1
	goto    bwait0
	return
	endm

カウンタを増やし、値を LED に表示する

カウンタの値の上限値はマクロのパラメータで与えること にします。 なお、表示パターンを与える getpat サブルーチンが必要です。


counter_init macro
        cblock
	counter
        endc
        clrf   counter
	call	dispcounter
        endm
counter_prg macro limit
inccounter
	incf    counter,1
	movlw   limit
	subwf   counter,0
	btfsc   STATUS,Z
        clrf    counter

dispcounter
        movf    counter,0
	call	getpat
	movwf	PORTB
	return
	endm

パターン取得

パターンは連続したビットパターンと 16 進の値を作りましたので、二つ になります。 なお、後々の利用のため、数字のパターンでの小数点のビットは 0 にしておきます。


getpat_bit_prg macro
getpat	
        addwf   PCL,1
pat_start
	retlw   b'00000000'
	retlw   b'00000001'
	retlw   b'00000011'
	retlw   b'00000111'
	retlw   b'00001111'
	retlw   b'00011111'
	retlw   b'00111111'
	retlw   b'01111111'
	retlw   b'11111111'
pat_end
        endm
getpat_num_prg macro
getpat	
        addwf   PCL,1
pat_start
	retlw   b'11111010' ; 0 このパターンは配線によって変わります
	retlw   b'10000010' ; 1
	retlw   b'11011100' ; 2
	retlw   b'11001110' ; 3
	retlw   b'10100110' ; 4
	retlw   b'01101110' ; 5
	retlw   b'01111110' ; 6
	retlw   b'11100010' ; 7
	retlw   b'11111110' ; 8
	retlw   b'11101110' ; 9
	retlw   b'11110110' ; A
	retlw   b'00111110' ; b
	retlw   b'01111000' ; C
	retlw   b'10011110' ; d
	retlw   b'01111100' ; E
	retlw   b'01110100' ; F
pat_end
        endm

4-4. マクロを使用した演習のプログラム

ここまでで定義したマクロをgs2.incという名前で 保存します。 そして、プログラムから #include "gs2.inc" で呼び出すことにし ます。 今回提示したプログラムのマクロ使用版を示します。

例4-1


;************
;*  例4-1 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
main
	movlw   b'01010101' ; 点灯させるビットパターン
	movwf   PORTB
        goto    $
	end

例4-2


;************
;*  例4-2 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
main
	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	iorwf   PORTB,1
        goto    $
	end

演習4-1


;************
;*  演習4-1 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
	enable_PB
main
	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	andwf   PORTB,1
        goto    $
	end

演習4-2


;************
;*  演習4-2 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
main
	movlw   b'01010101'
	movwf   PORTB
	movlw   b'00001111'
	xorwf   PORTB,1
        goto    $
	end

演習4-3


;************
;*  演習4-3 *
;************
        #include "gs2.inc"

	cblock 0x20
	 onpattern
	 offpattern
	endc
bitpattern equ b'00011111'

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PAPB

	movlw   bitpattern
        movwf   onpattern
	xorlw	b'11111111'
	movwf	offpattern
main
	movf    offpattern,0
	btfsc	PORTA,5
	movf    onpattern,0
	movwf	PORTB
        goto    main
	end

演習4-4


;************
;*  演習4-4 *
;************
        #include "gs2.inc"

bitpattern equ b'00011111'

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PAPB

	movlw   bitpattern
        movwf   PORTB
	sw_init

        sw_prg  reverse

reverse
	movlw	b'11111111'
	xorwf	PORTB,1
	return

	end

例4-3

1

;************
;*  例4-3 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
	bwait_init d'3'
main
        movlw   b'00000000'
	movwf	PORTB
	call    bwait
        movlw   b'00000001'
	movwf	PORTB
	call    bwait
        movlw   b'00000011'
	movwf	PORTB
	call    bwait
        movlw   b'00000111'
	movwf	PORTB
	call    bwait
        movlw   b'00001111'
	movwf	PORTB
	call    bwait
        movlw   b'00011111'
	movwf	PORTB
	call    bwait
        movlw   b'00111111'
	movwf	PORTB
	call    bwait
        movlw   b'01111111'
	movwf	PORTB
	call    bwait
        movlw   b'11111111'
	movwf	PORTB
	call    bwait
        goto    main

	bwait_prg
	end
2

;**************
;*  例4-3 2 *
;**************
        #include "gs2.inc"

display macro pattern
        movlw   pattern
	movwf	PORTB
	call    bwait
	endm

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
        bwait_init  d'3'
main
        display b'00000000'
        display b'00000001'
        display b'00000011'
        display b'00000111'
        display b'00001111'
        display b'00011111'
        display b'00111111'
        display b'01111111'
        display b'11111111'
        goto    main

	bwait_prg
	end
3

;**************
;*  例4-3 3 *
;**************
        #include "gs2.inc"

	cblock 0x20
	counter
	pattern
	endc


	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
        bwait_init  d'3'
main
        clrf    pattern
        movlw   1
	movwf   counter
loop
        movf    counter,0
        iorwf    pattern,1
        movf    pattern,0
        movwf   PORTB    ; 出力は一箇所
        call    bwait
	bcf     STATUS,C
        rlf     counter,1
	btfss	STATUS,C
	goto    loop
	goto	main

	bwait_prg
	end
4

;**************
;*  例4-3 4 *
;**************

        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
        bwait_init  d'3'
        counter_init
main
        call   bwait
        call   inccounter
        goto   main

        counter_prg pat_end-pat_start

        getpat_bit_prg

        bwait_prg       
	end

演習4-5


;************
;*  演習4-5 *
;************
        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PB
        bwait_init  d'3'
        counter_init
main
        call   bwait
        call   inccounter
        goto   main

        counter_prg pat_end-pat_start

        getpat_num_prg

        bwait_prg       
	end

演習4-6


;************
;*  演習4-6 *
;************

        #include "gs2.inc"

	org	0x0000
	goto	start
	org	0x0008
start
        enable_PAPB
        counter_init
	sw_init

	sw_prg  inccounter

        counter_prg pat_end-pat_start

        getpat_num_prg
	end

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科