キャリーを含めた減算命令の実装

こんにちは、めのんです!

前回に引き続き、今回も減算命令を実装していきます。
前回と違うのは、今回は繰り下がりを考慮した減算だということです。
繰り下がりにも繰り上がりと同様、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 個が埋まりました。

それでは次回またお会いしましょう!