HELP Contents
このPMLアセンブラでアセンブルできるアセンブリ言語は次のような仕様になっています。
PML用アセンブリ言語の基本構文は次のようになっています。
行ラベル: 命令 パラメータ1 パラメータ2 ;コメント
別にそれで区切りになるように作ることもできたんですが―――こういう物を扱う場合、世の中は基本的に全角文字ではアウトなので、あえて全角スペースとかがあっても区切りにならずエラーになるようにしています。
例えば以下のように書いてください。
>loop: LD X #200h ;X番地に200hを代入する LD Y #300h ;Y番地に300hを代入する ADD X Y ;X番地の値とY番地の値を加算する SUB, X, #150h ;このようにカンマで区切ってもよい JP >loop ;loopにジャンプする
JP >loop: ;loopにジャンプする
>loop:, LD, X, #200h ;行ラベルのコロンの後にカンマは書けません。
ニーモニックのパラメータは本文にもあるように、以下の書き方が可能です。
書き方 | 意味 |
---|---|
#nnn | #で始まるパラメータは即値すなわち、その値そのものとして扱われます。 |
@nnn | @で始まるパラメータも即値すなわち、その値そのものとして扱われます。 |
nnn | 数値をただ書いた場合は参照値、すなわちその数値の示すアドレスに書かれている値として扱われます。 |
[ nnn ] | このように角カッコで囲まれている場合は間接参照すなわち、そのアドレスに書かれた値のアドレスに書かれている値として扱われます。 |
アセンブラがパラメータを解釈する際に、その文字列がラベルとして定義されていればその値、定義されたラベルの中になければそれは数値表現であると見なして値を設定します。
数値表現の場合、まず末尾の文字を見てそれがどういう数値かを判断しています。
表記 | 意味 |
---|---|
nnn | nが 0-9 の数字で末尾に特に何も付けなれば10進数として解釈される。 |
nnnd | nが 0-9 の数字で10進数であることを強調したければ最後にdをつける。 |
nnnh | nが 0-9 および文字 A-F で、最後がhで終わればそれは16進数と解釈される。 |
nnnb | nが数字の0または1で、末尾がbで終われば2進数と解釈される。 |
n$ | nが任意の1文字で$で終わる場合はその文字のシフトJISの文字コードになる。 |
以下は実際の表現例です。
表記 | 実際の(10進数での)値 | |
---|---|---|
100 | 10進数表現 | 100 |
100d | 10進数表現 | 100 |
100h | 16進数表現 | 256 |
100b | 2進数表現 | 8 |
aabbh | 16進数表現 | 43,707 |
あ$ | 文字コード表現 | 33,440(82A0h) |
a$ | 文字コード表現 | 97(61h) |
ab$ | 文字コード表現 | 24,930(6162h) ←半角文字二つというのも実は○ |
文字列などを扱いたい場合はn$形式が便利ですが、以下の種類の文字はそのままでは表現できません。そのような文字を扱いたい場合は以下のようなエスケープ表記をするか、文字コードを直接値として使います。
エスケープ表記 | 文字コード | 意味 |
---|---|---|
\t | 09h | タブ |
\r | 0Dh | 復帰コード |
\n | 0Ah | 改行コード |
\s | 20h | スペース |
\c | 2Ch | 半角カンマ |
\q | 27h | 半角シングルクォーテーション |
ラベルとは本書内でも説明したとおりある数値と対応する文字列のことです。
ラベルの定義方法には下記の2種類があります。
EQU疑似命令とは例えば以下のような物で
EAST EQU 90
これは"EAST"という文字列に"90"という値を割り当てます。
またこのEASTという文字列は即値であるという気持ちをこめて
#EAST EQU 90
と定義してもOKです。
このあたりの事情については5.4ラベルが多すぎる!を参照して下さい。
行ラベルは
>loop: LD X #200h ;X番地に200hを代入する
とプログラム行頭に書かれたラベルで、その値はその行の命令が入っているメモリアドレスをアセンブラが自動計算してセットします。上記の例ではLDという命令の入っているメモリアドレスが>loopの値になります。
使える文字の変な制限や行ラベルを>loopなどとしているのには結構深い理由があって、これらについても詳しくは5.4ラベルが多すぎる!を見て下さい。
LD X #100h ;X番地に100hを代入する >loop: LD X #200h ;X番地に200hを代入する
ラベルを記述する際に以下のように増分パラメータを付加することができます。
#Y EQU 100 LD X #Y ;Xに値100をセット LD X #Y+1 ;Xに値101をセット LD X #Y-1 ;Xに値99をセット
すなわち"ラベル文字列+n"、"ラベル文字列-n"と記述することでそのラベルの値にnだけ加算、または減算された値を参照できます。
nは10進数で、0〜65535の範囲の数字でなければなりません。例えば下記のように書いたらエラーになります。
LD X #Y+10h ←エラー
これを使えば例えば以下のようなラベルを作りたいような場合
X0: DW 0 X1: DW 0 X2: DW 0 LD X0 #10 LD X1 #20 LD X2 #30
このような記述が可能になります。
X: DW 0, 0, 0 LD X+0 #10 LD X+1 #20 LD X+2 #30
行ラベルの形式で数値(正確には0〜9の数値で始まる文字列)を書くと、それはその行の絶対アドレス指定と解釈されます。
すなわち以下のように書けば、この命令は強制的に100番地に書き込まれ、以下の例ならばADD, X, YのADDは100番地になり、次の行のSUBが103番地になります。
100: ADD X Y SUB X Z
ですが、これは本文で行番号付きで説明しているソースを動かすために導入したもなので、自分でプログラムするときはあまり意味がないというより、使わない方が無難です。何かに強制されて、命令やデータをとにかくメモリの指定されたアドレスに置かなければならない場合にのみ使用して下さい。
PML命令は以下の種類が使用可能です。
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
1009h | LD | X | #Y | ---- | 第1パラメータのアドレスに第2パラメータで指定される値をセットする |
100Ah | LD | X | Y | ---- | |
100Bh | LD | X | [Y] | ---- | |
100Dh | LD | [X] | #Y | ---- | |
100Eh | LD | [X] | Y | ---- | |
100Fh | LD | [X] | [Y] | ---- |
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
2002h | NEG | X | --S- | パラメータの符号を反転する | |
2003h | NEG | [X] | --S- | ||
2109h | ADD | X | #Y | CZSV | 第1パラメータと第2パラメータを加算して結果を第1パラメータのアドレスにセットする |
210Ah | ADD | X | Y | CZSV | |
210Bh | ADD | X | [Y] | CZSV | |
210Dh | ADD | [X] | #Y | CZSV | |
210Eh | ADD | [X] | Y | CZSV | |
210Fh | ADD | [X] | [Y] | CZSV | |
2209h | SUB | X | #Y | CZSV | 第1パラメータから第2パラメータを減算して結果を第1パラメータのアドレスにセットする |
220Ah | SUB | X | Y | CZSV | |
220Bh | SUB | X | [Y] | CZSV | |
220Dh | SUB | [X] | #Y | CZSV | |
220Eh | SUB | [X] | Y | CZSV | |
220Fh | SUB | [X] | [Y] | CZSV | |
2219h | CMP | X | #Y | CZSV | 第1パラメータから第2パラメータを減算してフラグだけを設定する。主に値の比較に使う。 |
221Ah | CMP | X | Y | CZSV | |
221Bh | CMP | X | [Y] | CZSV | |
221Dh | CMP | [X] | #Y | CZSV | |
221Eh | CMP | [X] | Y | CZSV | |
221Fh | CMP | [X] | [Y] | CZSV |
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
3002h | NOT | X | -ZS- | パラメータの値をビット単位で論理否定する | |
3003h | NOT | [X] | -ZS- | ||
3109h | OR | X | #Y | -ZS- | 第1パラメータと第2パラメータのビット単位での論理和を第1パラメータのアドレスにセットする |
310Ah | OR | X | Y | -ZS- | |
310Bh | OR | X | [Y] | -ZS- | |
310Dh | OR | [X] | #Y | -ZS- | |
310Eh | OR | [X] | Y | -ZS- | |
310Fh | OR | [X] | [Y] | -ZS- | |
3309h | AND | X | #Y | -ZS- | 第1パラメータと第2パラメータのビット単位での論理積を第1パラメータのアドレスにセットする |
330Ah | AND | X | Y | -ZS- | |
330Bh | AND | X | [Y] | -ZS- | |
330Dh | AND | [X] | #Y | -ZS- | |
330Eh | AND | [X] | Y | -ZS- | |
330Fh | AND | [X] | [Y] | -ZS- | |
3209h | XOR | X | #Y | -ZS- | 第1パラメータと第2パラメータのビット単位での排他的論理和を第1パラメータのアドレスにセットする |
320Ah | XOR | X | Y | -ZS- | |
320Bh | XOR | X | [Y] | -ZS- | |
320Dh | XOR | [X] | #Y | -ZS- | |
320Eh | XOR | [X] | Y | -ZS- | |
320Fh | XOR | [X] | [Y] | -ZS- | |
3319h | BIT | X | #Y | -ZS- | 第1パラメータと第2パラメータのビット単位での論理積をとってフラグだけを設定する。主に値の各ビットのチェックに使う。 |
331Ah | BIT | X | Y | -ZS- | |
331Bh | BIT | X | [Y] | -ZS- | |
331Dh | BIT | [X] | #Y | -ZS- | |
331Eh | BIT | [X] | Y | -ZS- | |
331Fh | BIT | [X] | [Y] | -ZS- |
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
4002h | JP | X | ---- | パラメータの指定するアドレスにジャンプする | |
4003h | JP | [X] | ---- | ||
410Ah | JPF | X | Y | ---- | 第2パラメータが0だった場合第1パラメータのアドレスにジャンプする |
410Bh | JPF | X | [Y] | ---- | |
410Eh | JPF | [X] | Y | ---- | |
410Fh | JPF | [X] | [Y] | ---- | |
420Ah | JPT | X | Y | ---- | 第2パラメータが0でなかった場合第1パラメータのアドレスにジャンプする |
420Bh | JPT | X | [Y] | ---- | |
420Eh | JPT | [X] | Y | ---- | |
420Fh | JPT | [X] | [Y] | ---- |
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
5009h | IN | X | #Y | ---- | 第2パラメータのポートから第1パラメータのアドレスにデータを入力する |
500Ah | IN | X | Y | ---- | |
500Bh | IN | X | [Y] | ---- | |
500Dh | IN | [X] | #Y | ---- | |
500Eh | IN | [X] | Y | ---- | |
500Fh | IN | [X] | [Y] | ---- | |
5105h | OUT | #X | #Y | ---- | 第2パラメータの値を第1パラメータのポートに出力する |
5106h | OUT | #X | Y | ---- | |
5107h | OUT | #X | [Y] | ---- | |
5109h | OUT | X | #Y | ---- | |
510Ah | OUT | X | Y | ---- | |
510Bh | OUT | X | [Y] | ---- | |
510Dh | OUT | [X] | #Y | ---- | |
510Eh | OUT | [X] | Y | ---- | |
510Fh | OUT | [X] | [Y] | ---- |
機械語 | ニーモニック | フラグ | 意味 | ||
---|---|---|---|---|---|
6002h | PUSH | X | ---- | パラメータの値を待避する | |
6003h | PUSH | [X] | ---- | ||
6102h | POP | X | ---- | 待避した値をパラメータのアドレスに復帰する | |
6103h | POP | [X] | ---- | ||
7002h | SL | X | CZS- | パラメータの値を1ビット左にシフトする | |
7003h | SL | [X] | CZS- | ||
7102h | SR | X | CZS- | パラメータの値を1ビット右にシフトする | |
7103h | SR | [X] | CZS- | ||
8002h | CALL | X | ---- | パラメータのアドレスのサブルーチンを呼び出す | |
8003h | CALL | [X] | ---- | ||
8100h | RET | ---- | サブルーチンから復帰する | ||
FFF0h | HALT | ---- | プログラムの終了 | ||
0000h | NOP | ---- | 何もしない |
アセンブリ言語上では疑似命令というものが使える場合があります。これは一見命令風に見えますが機械語命令には翻訳されない命令のことで、アセンブリ言語を記述する際に様々な便利機能を実現するための命令です。
ラベルを定義するための命令です。
(#)ラベル EQU 値 (;コメント)
X EQU 245 ;Xというラベルが245という値を持つようになる
ラベルを記述する文字には通常#は使えませんが、EQU疑似命令のときに限って以下のような記述が可能です。
#X EQU 245 ;Xというラベルが245という値を持つようになる
こう記述した場合Xというラベルには即値属性がついて、うっかり即値以外で使おうとしたらエラーが出るようになっています。
例えば下の例ではYにXの値、すなわち245を代入しているように見えますが、実は245番地の値を代入する結果になっています!
LD Y X ←ERROR LD Y #X ←OK
EQU疑似命令は通常プログラムで使う様々な定数を定義するために使い、これでメモリアドレスを直指定するようなことはないはずです。そこでEQU疑似命令を利用する際は#付きで定義して、使用するときも#付きで使用する、としておくと間違いを減らすことができます。
―――と言いつつ、付属のサンプルプログラムではいきなりEQUでメモリアドレスを定義していますが、これは本書の説明の都合上絶対アドレスで記述していたためです。いわば邪道なので絶対真似しないで下さい。
あらかじめメモリにデータをセットしておく疑似命令です。
(行ラベル:) DW p1, (p2, p3, ...) (行ラベル:) DW, p1, (p2, p3, ...) ;こうやって全部カンマで区切ってもOK
この疑似命令を使うとプログラムのロード時にその位置にパラメータの値がメモリに順番に格納されていきます。
DW 10, 10h, 10b, A$
この例では十進法で 10, 16, 2, 65 がメモリにセットされます。
#P EQU 100 Q: DW #P, Q
こちらの例では#Pのところには100という値が、Qのところにはこの疑似命令のアドレス、すなわち#Pと書かれたメモリアドレスがセットされます。
データに文字列を使いたいような場合、以下のように半角のシングルクォーテーションで囲んで文字列として書くことができます。
DW '今日は朝から夜だった'
これは以下のように書くのと同じです。
DW 今$, 日$, は$, 朝$, か$, ら$, 夜$, だ$, っ$, た$
この文字列記法をする場合、文字列内にスペースやタブが入っていても構いませんが、シングルクォーテーション及び半角カンマがあってはなりません。その場合は1.1.4節で説明したようにエスケープ表記を利用して下さい。
DW 'I don't have CAT'S EYE' ←エラーになります DW 'I don\qt have CAT\qS EYE' ←OK DW 'その値は10,000だった' ←エラーになります DW 'その値は10\c000だった' ←OK
DW疑似命令には通常の命令同様に行ラベルが指定できます。
X: DW 10, 20
例えばこのように記述しておくとXというラベルが10という値の入ったメモリアドレスを指します。
すなわちプログラムで使用するためのメモリをこうして確保して、しかも初期値を入れておくこともできます―――すなわち高級言語における変数定義のようなもので、これがDW疑似命令の最も一般的な利用方法です。
本書のサンプルプログラムではプログラム中で値を入れておくメモリを例えば1000番地とか直接指定していましたが、その番地が本当に空いているかどうか確認するのは面倒な作業です。また初期値設定したい場合はLD命令でいちいち設定する必要がありました。
しかしDW疑似命令のラベルは通常の命令文の行ラベルの仕組みと同じなので、上記の例のXの値はアセンブラが自動的に計算してセットしてくれます。そのためそのアドレスが空いているかどうかいちいち気にする必要がありません。またメモリには同時に10という値が最初にセットされるので、初期値設定の必要がありません。
なお以下のように書けばアドレス直指定の機能が働いて、メモリの250〜254番地に1,2,3,10,12の値がセットされます。
250: DW 1,2,3,10,12
この命令が書かれた場所にパラメータで指定されるファイルを挿入します。
INCLUDE filename
この命令を使うと命令の書かれた場所に指定されたファイルを挿入できます。
例えば以下のような内容の二つのファイルを作ります。
<Prog1.pasm>
ADD X Y ;XとYを加算 SR X ;Xを2で割る
<Prog2.pasm>
LD X #15 LD Y #25 INCLUDE "Prog1.pasm" CMP X Y JPT >Z ZF
そしてProg2.pasmを読み込むと、以下のようにINCLUDE疑似命令が書かれた行にProg1.pasmの内容がそのまま、コメントまで含めて挿入されます。
LD X #15 LD Y #25 ▼INCLUDE Prog1.pasm ADD X Y ;XとYを加算 SR X ;Xを2で割る ▲END INCLUDE CMP X Y JPT >Z ZF
この命令は複数のプログラムで共通の定義やサブルーチンを共有するために便利です。
プログラムを「パーツ化」する機能です。
ラベル: MACRO (仮パラメータ1 (,仮パラメータ2 (, ...)) マクロソースの記述 ENDMACRO
定義されたマクロは下記のように記述することで、定義された内容が呼び出された場所に展開されて、仮パラメータが実パラメータに置換されます。
ラベル 実パラメータ1, 実パラメータ2, ...
例えば以下のマクロの場合
&IFP_: MACRO %L, ?L0 JPT ?L0 ZF ;結果 > 0なら%Lへ JPT ?L0 SF JP %L ?L0: ENDMACRO
以下のように記述すると
SUB X Y &IFP_ >L LD X #0 >L: 続きの処理
このように展開されます。
SUB X Y JPT *xxxx ZF ;結果 > 0なら>Lへ JPT *xxxx SF JP >L *xxxx: LD X #0 >L: 続きの処理
ローカルラベルが使えるブロックを定義します。
(行ラベル:)PROC ENDPROC
上記のようにPROCとENDPROCで囲まれた範囲で定義されたラベルはローカルラベルになります。
例えば以下のようにラベルが定義されているとします。
#X EQU #10 ;(1) FN: PROC #X EQU #20 ;(2) LD P #X ;(3) LD Q #X ;(4) P: DW 0 ;(5) ENDPROC LD P #X ;(6) P: DW 0 ;(7) Q: DW 0
この例ではXおよびPというラベルが2カ所で定義されています。
すなわちあたかもPROC内で定義されたラベルはそのPROC内でのみ有効として扱うことができます。
プログラムを擬似的にモジュール化します。
unitname:UNIT モジュール内容 ENDUNIT
上記のようにUNIT疑似命令とENDUNIT疑似命令で囲まれたブロックは疑似モジュールとなります。汎用的なサブルーチンをまとめておきたいような場合に使います。
ユニット内のラベルをアクセス可能にします。
USES unit1(, unit2, ...)
プログラムからUNIT内のラベル(主にサブルーチンやグローバルな変数など)にアクセスしたい場合USES疑似命令を使ってユニット名を記述しておくと、そのユニット内のラベルを参照することができるようになります。
アセンブルエラーがあった場合メニュー下の情報表示欄に赤太字でエラーの数が表示され、エラーのあった行のコメント欄に以下のようなメッセージが出ます。
[ソースファイル名(エラー行)]エラーメッセージ
エラーを修正するにはエラー行を右クリックしてソースの編集(E)を選択します。ソースファイルが設定されているエディタで開かれて、エディタが行番号ジャンプに対応していればその行にカーソルが飛びます。
そこで修正を行ったあとエディタ上で上書き保存を行い、PMLエミュレータからファイル(F)/読み込み直す(R)を実行すると修正ファイルがロード・アセンブルされます。
以下はアセンブルエラーに付随するエラーメッセージです。
プログラムの命令はPML命令と疑似命令にあるもの以外は使用できません。
このエラーが出た場合は命令の綴り間違い、ラベルの":"を書き忘れた場合、定義されていないマクロを呼び出した場合などがあります。
PML命令は各々パラメータの必要数が決まっていますが、その数が合わない場合にこのエラーが出ます。
引用されたラベルが定義されていないため、値に変換ができません。
PROCのブロック内で定義されているラベル、USES疑似命令でアクセス指定されていないユニット内で定義されているラベルを参照しようとした場合もこのエラーが出ます。
そういったラベルを参照しなければならない場合はネームパスをつけるか、ユニットの場合はUSESでアクセス設定をしておく必要があります。
EQUでラベルを定義する際の数値の書式に問題があります。
PML命令で利用できないパラメータの組み合わせが使われています。
例えばLD命令の第一パラメータに即値を使用しているような場合です。どういう組み合わせが可能なのかは5.3節を参照して下さい。
また[#X]のように間接参照内に即値を書いたような場合にもこのエラーが出ます。
絶対アドレス指定を行っている場合に、既に定義された領域のアドレスが指定された場合にこのエラーが出ます。
同じ名前のラベルが再度定義された場合このエラーが出ます。
プログラムが小さいうちはいいのですが、大きくなってくるとすぐにラベル名の衝突が起こります。これを防ぐにはPROCやUNIT、マクロのオートラベルなどの機構を利用して下さい。
ラベルに使用できる文字は A-Z a-z > ^ _ * です。これ以外の文字が使われていたらこのエラーが出ます。
EQUで定義した#付きの即値ラベルを参照する際に#が付加されていません。
PROC〜ENDPROC、UNIT〜ENDUNITの対応がおかしくなっています。
ユニット定義を行っているところでユニット名が設定されていません。
マクロ定義をしているところでマクロラベルが設定されていません。
引用されたマクロラベルが重複しています。
マクロの仮引数が%もしくは?で始まっていません。
マクロ定義中にさらにMACRO疑似命令が現れました。
前のマクロでENDMACROを書き忘れた場合にもこのエラーになります。
マクロ疑似命令がないのにENDMACRO疑似命令が現れました。