第8章
式
命令や疑似命令が数を必要とする場所には、どこでも式 (expression)を書くことができます。式とは、数、名前、またはそれらを組み合わせた計算のことで、10、msg、msg+2、(count-1)*4などがその例です。TATARAは、その行をアセンブルするときに式の値を計算します。この章では、数と文字の書き方、演算子の働きと計算の順序、そして式が持つ値の種類を説明します。例には、それぞれの式の値を2バイトで格納するDWを使います(11章)。
8.1 値は16ビット
TATARAが計算する値は、すべて0から65535(0FFFFh)までの16ビットの数です。どちらかの端を越える計算は、反対側に回り込みます。65535+1は0になり、0-1は0FFFFhになります。そのため、負の数は、Z80自身が扱うのと同じように、16ビットの補数として格納されます。-1は0FFFFh、-6は0FFFAhです。
ld a,…やDBのオペランドのように、1バイトしか必要としない場所では、TATARAは値の下位バイトを使います。そこで受け付けられる値については、9章と11章で説明します。
8.2 数
数は、表8.1のように、後ろに付けた文字がほかの基数を示さないかぎり、10進数です。この文字は大文字でも小文字でもかまいません。
| 基数 | 接尾辞 | 例 | 値 |
| 10進数 | なし、またはD | 100、100d | 100 |
| 16進数 | H | 100h | 256 |
| 8進数 | OまたはQ | 100o、100q | 64 |
| 2進数 | B | 100b | 4 |
数は必ず数字で始まります。英字で始まる語は名前です(7章)。そのため、最初の桁が英字になる16進数には、前に0を付ける必要があります。0ffhは255ですが、ffhは名前なので、定義していなければTATARAは次のように表示して止まります。
HEXNAME.AS(2): ERROR: undefined symbol in an expression.
警告. BとDは16進数の数字であり、接尾辞でもあります。どちらかで終わる数は、2進数または10進数として読まれます。10bは10Bhではなく2です。16進数は、0abhや12bhのように、必ずHで終えてください。
12bの2のように、その基数にない数字があると、TATARAは止まります。
BADDIGIT.AS(2): ERROR: bad expression.
8.3 文字
文字定数(character constant)は、アポストロフィーまたは引用符で囲んだ1文字か2文字です。その値は文字のコードで、’A’と"A"はどちらも41hです。2文字の場合は、最初の文字が上位バイトに入るので、"AB"は4142hになります。
2種類の引用符は同じ働きをするので、囲みたい文字がそのどちらかのときに便利です。"’"はアポストロフィーです。また、定数の中では、それを囲んでいる記号を2つ続けて書くと、その記号そのものを表します。そのため、””もアポストロフィーです。
3文字以上の文字列はDBで使います(11章)。
8.4 名前とロケーション・カウンター
式の中の名前は、その値を表します。ラベルのアドレス、またはEQUやDEFLで与えた数です(12章)。TATARAはソースを2回読むので、名前は定義する行より前でも使えます。次のソースでは、jp laterが正しくアセンブルされます。
cseg jp later ; defined below later: ret
どこにも定義されていない名前があると、TATARAは止まります。
UNDEF.AS(2): ERROR: undefined symbol in an expression.
$だけのときは、ロケーション・カウンター (location counter)を表します。これは、その行のいまのセグメントでのアドレスです(10章)。よく使われるのはjr $で、自分自身にジャンプして、そのまま待ち続けます。
8.5 演算子
演算子を表8.2に示します。英語の単語で書く演算子は、大文字でも小文字でも書けますが、両側にスペースかかっこが必要です。aandbではなくa and bと書きます。
| 演算子 | 働き |
例と値 |
| + - | 加算、減算 |
10-4-3 = 3 |
| * / | 乗算、除算 |
17/5 = 3 |
| MOD | 除算の余り |
17 mod 5 = 2 |
| SHL SHR | 左シフト、右シフト |
1 shl 4 = 10h |
| AND OR XOR | ビットごとの論理積、 |
0f0h and 3ch = 30h |
| NOT | すべてのビットを反転 | not 0 = 0FFFFh |
| HIGH LOW | 上位バイト、下位バイト |
high 1234h = 12h |
| EQ NE | 等しい、等しくない |
1 eq 1 = 0FFFFh |
| LT LE | より小さい、以下 |
2 le 1 = 0 |
| GT GE | より大きい、以上 |
2 gt 1 = 0FFFFh |
| -(値の前) | 符号反転 |
-1 = 0FFFFh |
いくつかの演算子には、補足が必要です。
- 比較の演算子は、真なら0FFFFh、偽なら0を返します。そのため、その結果をAND、OR、NOTで組み合わせることができます。比較は符号なしの数として行われます。-1は0FFFFhなので、-1 gt 1は真です。
- 0で割ってもエラーにはなりません。M80と同じように、結果は0FFFFhです。
- HIGHとLOWは、すぐあとの値にだけ働きます。high 1234h+1は(high 1234h)+1で、13hです。
8.5.1 優先順位
演算子が2つ以上ある式では、それぞれの優先順位 (precedence)によって、どれを先に計算するかが決まります。表8.3に、結び付きの強いものから順にその段階を示します。同じ段階の演算子は左から右へ計算されるので、10-4-3は(10-4)-3です。かっこを使うと順序を変えられます。2+3*4は 14で、(2+3)*4は20です。
| 段階 | 演算子 |
| 1 | HIGH LOW |
| 2 | * / MOD SHL SHR |
| 3 | 値の前の- |
| 4 | + - |
| 5 | EQ NE LT LE GT GE |
| 6 | NOT |
| 7 | AND |
| 8 | OR XOR |
これはM80と同じ段階です。覚えておくとよいことが2つあります。-2*3は-(2*3)で、not a eq bはnot (a eq b)です。迷ったときは、かっこを付けてください。かっこには何の負担もありません。
8.6 値の種類
2章で説明したように、TATARAはすべてのアドレスを知ることはできず、TANRENが埋めるための穴を残します。そのため、どの式も、値だけでなく種類を持っています。
- アブソリュート
-
10、’A’、EQUで定義した名前のように、TATARAがアセンブルした時点で完成している数です。このような値をアブソリュート(absolute)といいます。
- リロケータブル
-
コード・セグメントのラベルのような、セグメントの中のアドレスです。TATARAが知っているのはセグメントの先頭からの距離だけで、セグメントを置いたアドレスはTANRENが加えます。
- 外部
-
EXTRNで宣言した、ほかのモジュールが定義する名前です。TATARAはその値について何も知らず、TANRENが埋めます。
式が役に立つのは、TANRENがアドレスを1つ加えるだけで完成させられる場合だけです。そのため、TATARAが受け付けるのは表8.4の組み合わせだけです。それ以外は、両側ともアブソリュートでなければなりません。
式 |
結果 | 例 |
アブソリュート+リロケータブル、 |
リロケータブル | a1+2 |
リロケータブル−アブソリュート | リロケータブル | a1-2 |
リロケータブル−リロケータブル | アブソリュート | a2-a1 |
外部+アブソリュート、またはその逆 |
外部 | far+2 |
外部−アブソリュート |
外部 | far-2 |
このうちもっとも役に立つのは、同じセグメントの2つのラベルの差です。これは長さを表し、セグメントをどこに置いても変わりません。次のソースでは、a2-a1は1、a1+2はa1の2バイト後ろのアドレスですが、a1*2はコードを移動すると意味がなくなります。
cseg a1: nop a2: nop dw a2-a1 ; absolute: 1 dw a1+2 ; relocatable dw a1*2 ; not allowed
RELOC.AS(6): ERROR: relocation error - a segment-relative value is not allowed here.
外部の名前は、1つの式の中で1回だけ、数を加えるか引くかして使えます。2-farは受け付けられません。
EXTRN.AS(5): ERROR: an external symbol may not be used here.
符号反転、NOT、HIGH、LOWにも、アブソリュートの値が必要です。また、セグメントはそれぞれが1つの種類なので、別々のセグメントにある2つのラベルを引き算することもできません(10章)。