アセンブラでプログラミングを行う際には、
多少のCPUに対する知識が必要になります。
慣れるまでは色々大変でしょうが、慣れてしまえば、
全く問題なく開発することができます。
アセンブラなんてわかんね〜ぜ!!」という人も、
やる前からあきらめるのではなく、まずは挑戦してみることからはじめてください。


まず、アセンブラでプログラムを書く際の注意点を述べます。
高級言語と違い、アセンブリ言語では、常に、メモリの状態を意識する必要があります。
特に、ワンチップマイコンでは、あるメモリの番地にデータを書き込むことで、
外部に5Vの電圧を出力したり、逆に入力に設定したりします。
おかしな書き方をすると、最悪の場合壊れたりすることもあります。
メモリの番地には最大限の注意を払ってください。

次に、これはCなどの高級言語でもいえることですが、
課題で求められている動作を分析し、CPUが実行すべき動作を、
フローチャートなどにまとめておいた方が良いでしょう。
そうすれば、上手く動作しない時に、どこがおかしいのかをすばやく見つけることができます。

さて、それではプログラミングについて説明することにしましょう。
まず、命令の種類について説明します。
アセンブリ言語の命令には、大まかに分けて、データ転送命令、算術演算命令、
論理演算命令、シフト命令、ビット操作命令、システム制御命令があります。
この中でも、よく使う命令となると、かなり限られてきます。
次に、命令の書き方について述べます。
演算、データ移動等の命令は、次のように書きます。

LABEL:  オペコード.サイズ  ソースオペランド,ディスティネーションオペランド


それでは1つ1つの項について説明します。
[LABEL:]
ラベルです。省略しても構いません。
どのようなときに使うかというと、
アセンブラは命令が書かれた順番に、メモリ番地の上から下へ命令を実行していきます。
しかし、C言語での、

for(i=0;i<100;i++);//iが100になるまでループ

というような処理を 行う際には、メモリ上のある場所へジャンプする必要があります。
戻りたい行にラベルを貼っておけば、分岐命令でラベルを指定して、ラベルの行に処理を戻すことができます。
サンプルプログラムでも

LP1: MOV.B #B'00001111,R0L ;ポート8に0x0fを出力
・・・・・・・・・・・・・・・・・・・・・・・・
  BRA LP1

のように使われていますね。BRAは無条件分岐命令で、
その行にきたら、指定したアドレスにジャンプする命令です。
ここではLP1を指定していますので、
LP1まで戻って、同じ処理を繰り返す事になります。
アセンブラで書いたプログラムのどの行がCPUのどのアドレスに割り付けられるかを知るのは
多少複雑ですので、分岐する際には、ラベルを用いて分岐することをお勧めします。
余談ですが、よく大学院入試の問題にこのあたりの問題が出ます。
今のうちにマスターしておけば、大学院に進学する際に有利かも?

[オペコード] これがアセンブラの命令です。
足し算、引き算などの算術演算から、分岐、シフト、データ転送などの命令があります。
これは後でもう少し詳しく説明します。

[.サイズ] データのサイズをあらわしています
これが.Bである場合は1バイト(8ビット)のデータを、.Wである場合には1ワード(16ビット)のデータを、
.Lの場合はロングワード(32ビット)をあらわします。
C言語において、データ型にchar型(8bit)、int型(16ビット)、long型(32ビット)等あったと思います。
扱う数値によって、変数の形を選びましたよね?
要するに、ASCII文字列や、0から255までの数値を扱う場合は.Bで、
0〜65535の数値を扱いたい場合は.Wで、0〜4294967295の数値を扱いたい場合は.L をつければよいのです。
"."を忘れないようにしてください。
気をつけなくてはならないのが汎用レジスタのビット長です。
バイトのデータを読み込む際には、バイトサイズのレジスタに、
ワードサイズならば、ワードサイズのレジスタに、 ロングワードのデータサイズならば、ロングワードサイズのレジスタに読み込んでください。
レジスタの大きさは以下のとおり。
ER0〜ER7:32ビット(8個)ER7はスタックポインタとして使用されるため、使用不可
E0〜E7、R0〜R7:16ビット(16個)
R0L〜R7L、R0H〜R7H:8ビット(16個)
のレジスタがあります。
実際に存在するレジスタはER0〜ER7の8個だけなので、
ER0に値をいれて、R0にも値を書き込んでしまえば、ER0に元から入っていたデータは
無くなってしまうので気をつけてください。
そのあたりのことはハードウェアマニュアルの2.2.1に詳しく書いてあるので参照して下さい。


[ソースオペランド,ディスティネーションオペランド]
これは処理をする際に必要なデータ、又はデータが格納されているメモリのアドレスを 指定します。
ソースオペランドには数値データを指定することができ、ディスティネーションオペランドには、
ほとんどの場合、レジスタか、メモリ番地を指定します。
命令の実行結果、例えば加算命令の結果はディスティネーションオペランドに書き込まれます。
メモリ番地の指定については、様々な方法があります。
慣れるに従ってだんだんとわかるようになりますので、
最初のうちはサンプルプログラムを見ながら、見よう見真似で書いてみてください。
メモリ番地(アドレス)の指定方式については、
ハードウェアマニュアルの2・5に詳しくかかれています。
数値データをあらわす際には、数値の初めに#をつけ、
その値が2進数か、10進数か、16進数かを表すために、#の後に、
2進数ならば、B'を、10進ならば何も書かずに、16進ならH'をつけてください。
以下に、オペランドの例を幾つか示します。

#B'1001:2進数で1001(9)を表します。
#1001:10進数で1001を表します。
#H'1001:16進数で1001(4097)を表します


命令の書き方の例を幾つかあげておきましょう。

ADD.B  #B'1001,R0L:R0Lの値と2進数1001を足した数をR0Lに書き込む
SUB.W  #1,R0:R0レジスタから1を引いた値をR0に書き込む
BSR   LP:ラベルLPがの位置までサブルーチン分岐する。

それでは以下で、命令の種類を見ていきます。

データ転送命令
MOV、POP、PUSH
データ転送命令のうち、最もよく使うのはMOVです。
アセンブラプログラムで使用されている命令のうち、半分くらいはMOV命令だといっても過言ではありません。
この命令は文字通り、レジスタ-レジスタ間や、レジスタ-メモリ間でデータを移動させる際に用います。
サンプルプログラムでも、何回か用いられてましたね。
例:
MOV.B #B'01111111,R0L
上の例では、2進数01111111を8ビットの汎用レジスタR0Lに読み込む命令です。

算術演算命令
ADD、SUB、INC、DEC、CMP、MULXS、NEG、DIVXS、・・・etc
算術演算命令です。
要するに加減乗除、比較等があります。
上で挙げたものの他にもいくつか命令があるので、
ハードウェアマニュアルを参照してください。

論理演算命令
AND、OR、EXOR、NOT
論理演算を行います。
AND:論理積
OR:論理和
EXOR:排他的論理和
NOT:否定
それぞれの命令はこのようになります。
具体的に、R0Lの内容が#B'1100、R0Hの内容が#B'1010である場合、
AND  R0L,R0H:R0LとR0Hの論理和
OR  R0L,R0H:R0LとR0Hの論理積
EXOR R0L,R0H:R0LとR0Hの排他的論理和
NOT  R0H:R0Hの否定
の結果は、すべてR0Hに反映され、
それぞれB'1000、B'1111、B'0110、B'0101となります。

シフト命令
SHAL、SHAR、SHLL、SHLR、ROTL、ROTR、ROTXL、ROTXR
これらはシフト命令です。
もし、レジスタR0Lの内容がB'1100だとすると、
SHAL R0L
SHAR R0L
の結果は、それぞれB'11000、B'0110になります。
シフトの結果起こる桁あふれを考慮する場合や、
考慮しなくてもよい場合に応じて使い分けてください。
シフト命令は、主に、2のべき乗で乗算、除算を行う場合に用います。
例を挙げれば、2進数で1000(10進数の8)を右に1ビットシフトすれば100(10進数で4)となり、
逆に左に1ビットシフトすれば10000(10進数で16)になり、2で除算、乗算したりすることができます。
算術演算に、乗算命令がありますが、一般的に乗算命令は、
シフトや、加算に比べてステート数(命令を実行し終えるまでの時間)がかかるので、
高速に乗除算を行う必要がある場合には、シフト命令と加減算命令をうまく組み合わせて実行します。

ビット操作命令命令
BST、BIST、BAND、・・・etc
あるレジスタや、メモリ上の1ビットだけを0にしたい場合などに使用します。
また、あるビットの状態が0であるか1であるかを判定する際にも用いますので、
詳しくはハードウェアマニュアルを参照してください。

分岐命令
Bcc、BSR、JMP、JSR、RTS
プログラムの実行中にある番地へジャンプしたい場合に使います。
C言語でも同じ処理を繰り返し行う際には、別の関数を作って、
その関数をmainから呼び出したりしますよね。
そのような処理を行いたい時に用います。
分岐には2種類あって、呼び出した元の場所に戻ってくる分岐と、
元に戻らず、ジャンプした先から実行を続ける分岐があります。
元の場所に戻ってくる必要がある際には、サブルーチン分岐命令を、
戻ってくる必要の無い場合はJMPなどの分岐命令を用いてください。
また、ある条件を満たしている場合だけ分岐するようにしたい場合は、条件分岐命令を用いてください。
サブルーチン内で元のルーチンに戻る場合にはRTS命令を用いてください。
これらの使い分けを誤ると、とたんにプログラムは動かなくなります。
分岐命令を用いる際には、それがどのような分岐であるかを把握する必要があります。

その他
.ORG、.INCLUDE、.DATA、.EQU、.END、・・・etc
これまで述べてきた命令は、すべてCPUの動作を記述するための命令でしたが、
アセンブラ解釈して実行する命令も存在します。
サンプルプログラムの上の方に

.CPU 300HN ;CPUの指定
.INCLUDE "h8_3664f.h"

のような記述が見られます。
.CPU・・・はアセンブラに用いるCPUを、
.INCLUDE・・・はアセンブル際に読み込むファイルを
それぞれ指定しています。
これらの命令は、アセンブラがソースファイルをアセンブルする際に
アセンブラに必要な情報を与える為に使われます。
ソフトウェアマニュアルの469ページ
に書いてありますので、一度目を通しておいてください。
しかし、今はC言語で、
#include<stdio.h>
と書いて、「これはコンパイラを動かす時のおまじない」と、とりあえずは覚えました。
これと同じようなものだと理解してください。








h8_3664f.h:
マイコンには、タイマや、アナログ信号をデジタル信号に変換する機能、
コンピュータとシリアル通信を行う機能などが内臓されています。
これらの機能は、内部の設定レジスタにある値を書き込む事で使えるようになります。
例を挙げれば、「タイマを動作させて、1秒間を正確に測りたい」
というような場合には、タイマ1を動かすためのレジスタに値を書き込まないといけません。
そのようなレジスタのアドレスをすべて記述してあるのが[h8_3664f.h]というヘッダーファイルです。
このファイルを開いたら、PDRAなどの単語に、どの番地を対応させるかが、びっちりと書き込まれています。
演習1の一番最初にコンパイルした際は、
サンプルプログラムの.INCLUDEの行に";"(セミコロン、まちがってもチョンチョンなどと読まないでください)をつけて、
コメント行にしているため、PDR8と書かれても、どこかわからないとアセンブラが文句を言っているのです。






おまじない:私が1年生だった時は、このように教わりました。





アセンブラなんてわかんね〜ぜ!:
大学生も3年生ともなると、
「Cならプログラミングもできるが、アセンブラなんてわかんね〜ぜ」
というような言い訳じみた事を言う人がいます。
こういう人は、「じゃあ、Cでいいから書いてみて」
と言っても、それはそれで間違っている場合が多い。
また、課題で煮詰まってくると、「アセンブラってなんでこんなにバカなの?」
という人がいるが、これは、大きな間違いである。
アセンブラは、あなたが書いたソースをただ機械語に変換しているだけなので、
アセンブラがバカだと言い放つ人は、天に唾するようなものです。









戻る