こんにちは、めのんです!
前回に引き続き、今回も減算命令を実装していきます。
前回と違うのは、今回は繰り下がりを考慮した減算だということです。
繰り下がりにも繰り上がりと同様、Cフラグ(キャリーフラグ)を使います。
SBC命令の実装
SBC命令は汎用レジスタどうしの減算をCフラグを含めて行います。
SUB命令同様、減算の結果は一方の汎用レジスタに格納します。
それではいつものように「AVR®命令一式手引書」から命令の説明を引用します。
ほとんどSUB命令と同じですが、実行前のCフラグの値を引くことと、Zフラグを求める際に実行前のZフラグとのANDを取っているところが異なります。
それではコードを見ていきます。
static void sbc(atmega328_t *cpu, uint16_t op)
{
int d = (op >> 4) & 0x1f;
int r = (op & 0xf) | (op >> 5) & 0x10;
int rd = cpu->r[d];
int rr = cpu->r[r];
int z = (cpu->sr & flag_Z) != 0;
int c = (cpu->sr & flag_C) != 0;
int value = rd - rr - c;
cpu->r[d] = value;
int sr = cpu->sr & ~(flag_H | flag_S | flag_V | flag_N | flag_Z | flag_C);
int v = (rd & 0x80) && !(rr & 0x80) && !(value & 0x80) || !(rd & 0x80) && (rr & 0x80) && (value & 0x80);
int n = value & 0x80;
if (!(rd & 0x08) && (rr & 0x08) || (rr & 0x08) && (value & 0x08) || (value &
0x08) && !(rd & 0x08))
sr |= flag_H;
if (n ^ v)
sr |= flag_S;
if (v)
sr |= flag_V;
if (n)
sr |= flag_N;
if ((value & 0xffff) == 0 && z)
sr |= flag_Z;
if (!(rd & 0x80) && (rr & 0x80) || (rr & 0x80) && (value & 0x80) || (value &
0x80) && !(rd & 0x80))
sr |= flag_C;
cpu->sr = sr;
++cpu->clock;
}
今回も素直に定義どおりにコーディングしただけです。
sbci命令の実装
次はSBCI命令です。
SUB命令に対するSUBI命令のように、SBC命令に対するSBCI命令のように対応しています。
こちらも「AVR®命令一式手引書」から命令の説明を引用します。
オペランドの取り方を除けばSBCI命令と同じですね。
これもコードを見ていきましょう。
static void sbci(atmega328_t *cpu, uint16_t op)
{
int d = 16 + ((op >> 4) & 0xf);
int K = (op & 0xf) | (op >> 4) & 0xf0;
int rd = cpu->r[d];
int z = (cpu->sr & flag_Z) != 0;
int c = (cpu->sr & flag_C) != 0;
int value = cpu->r[d] - K - c;
cpu->r[d] = value;
int sr = cpu->sr & ~(flag_H | flag_S | flag_V | flag_N | flag_Z | flag_C);
int v = (rd & 0x80) && !(K & 0x80) && !(value & 0x80) || !(rd & 0x80) &&
(K & 0x80) && (value & 0x80);
int n = value & 0x80;
if (!(rd & 0x08) && (K & 0x08) || (K & 0x08) && (value & 0x08) || (value & 0x08) && !(rd & 0x08))
sr |= flag_H;
if (n ^ v)
sr |= flag_S;
if (v)
sr |= flag_V;
if (n)
sr |= flag_N;
if ((value & 0xffff) == 0 && z)
sr |= flag_Z;
if (!(rd & 0x80) && (K & 0x80) || (K & 0x80) && (value & 0x80) || (value & 0x80) && !(rd & 0x80))
sr |= flag_C;
cpu->sr = sr;
++cpu->clock;
}
これも定義通りに実装しただけです。
op_tableへの登録
次に、いつものようにop_tableに登録します。
opcode.phpに次のコードを追加して実行してあげればOKです。
for ($i = 0; $i < 1024; ++$i)
{
$opcode_table[0b0000_1000_0000_0000 | $i] = 'sbc';
}
for ($i = 0; $i < 4096; ++$i)
{
$opcode_table[0b0100_0000_0000_0000 | $i] = 'sbci';
}
今回も65,536命令のうち 1,024 + 4,096 = 5,120 個が埋まりました。
それでは次回またお会いしましょう!