こんにちは、めのんです!
今回からいよいよ算術命令を実装していきます。
算術命令はステータスレジスタの振る舞いが複雑なのであと回しにしてきました。
でも、これ以上あと回しにはできませんので、順番に手を付けていきます。
INC命令の実装
まずはINC命令を実装します。
INC命令は汎用レジスタの値をインクリメントするための命令です。
インクリメント自体は+1するだけですので簡単ですが、冒頭でも書いたようにステータスレジスタの振る舞いが複雑なんです。
こんな感じでインクリメント専用の命令が備わっているアーキテクチャは多いので、Cでは++演算子のように普通の加算演算子とは別にインクリメント専用の演算子が設けられているんです。
それではいつものように「AVR®命令一式手引書」から命令の説明を引用します。
Vフラグの振る舞いが難しそうに見えますが、要するにインクリメントしたあとの値が0x80ならセット、それ以外ならリセットすればよさそうです。
あとはこれまで通りですね。
では、コードを見ていきます。
static void inc(atmega328_t *cpu, uint16_t op)
{
int d = (op >> 4) & 0x1f;
unsigned int value = (int8_t)cpu->r[d];
++value;
cpu->r[d] = (unsigned char)value;
int sr = cpu->sr & ~(flag_S | flag_V | flag_N | flag_Z | flag_C);
int n = (value & 0x80) != 0;
int v = value == 0x80;
if (n ^ v)
sr |= flag_S;
if (v)
sr |= flag_V;
if (n)
sr |= flag_N;
if ((value & 0xff) == 0)
sr |= flag_Z;
cpu->sr = sr;
++cpu->clock;
}
NフラグとVフラグさえ決まれば、あとは仕様書通り実装していくだけですね。
DEC命令の実装
次にDEC命令を実装します。
DEC命令は汎用レジスタの値をデクリメントする命令です。
インクリメント同様、デクリメント専用の命令が設けられているアーキテクチャは少なくありません。
C言語では–演算子が用意されていますね。
では、こちらも「AVR®命令一式手引書」から命令の説明を引用しながら説明していきます。
INC命令とDEC命令は対になる命令ですが、ステータスレジスタの振る舞いは微妙に異なるので要注意です。
では、コードを見ていきますね。
static void dec(atmega328_t *cpu, uint16_t op)
{
int d = (op >> 4) & 0x1f;
unsigned int value = (int8_t)cpu->r[d];
--value;
cpu->r[d] = (unsigned char)value;
int sr = cpu->sr & ~(flag_S | flag_V | flag_N | flag_Z | flag_C);
int n = (value & 0x80) != 0;
int v = value == 0x7f;
if (n ^ v)
sr |= flag_S;
if (v)
sr |= flag_V;
if (n)
sr |= flag_N;
if ((value & 0xff) == 0)
sr |= flag_Z;
cpu->sr = sr;
++cpu->clock;
}
今度はデクリメントした結果が0x7fの場合にVフラグをセットするようにしています。
あとはINC命令と同じですね。
op_tableへの登録
次に、いつものようにop_tableに登録します。
opcode.phpに次のコードを追加して実行してあげればOKです。
for ($d = 0; $d < 32; ++$d)
{
$opcode_table[0b1001_0100_0000_0011 | $d << 4] = 'inc';
$opcode_table[0b1001_0100_0000_1010 | $d << 4] = 'dec';
}
今回も65,536命令のうち 32 × 2 = 64 個が埋まりました。
それでは次回またお会いしましょう!