2試計算機>制御コードの実装

 今回は、減算・乗算系のコードとZeroフラグ系の制御コード(比較コードとZeroフラグがセットされていたら指定アドレスへジャンプするコード)を実装してみます。また、ジャンプ系コードもアドレッシング周りを整理しておきましょう。

減算・乗算系コードの実装

 今回追加する減算・乗算系コードは、以下の通りです。subが減算、mulが乗算で、形式は加算と全く同じですから演算の意味はすぐにわかっていただけるでしょう。

コード値定数ニーモックコードサイズ
20SUBRR32sub32 r1,r24 (0020r1r2)
21SUBRI8sub8 r1,Imm84 (0021r1Imm8)
22SUBRI32sub32 r1,Imm328 (0022r100,Imm32)
23SUBRM8sub8 r1,(r2)4 (0023r1r2)
24SUBRM16sub16 r1,(r2)4 (0024r1r2)
25SUBRM32sub32 r1,(r2)4 (0025r1r2)
26SUBMR32sub32 (r1),r24 (0026r1r2)
30MULRR32mul32 r1,r24 (0020r1r2)
31MULRI8mul8 r1,Imm84 (0021r1Imm8)
32MULRI32mul32 r1,Imm328 (0022r100,Imm32)
33MULRM8mul8 r1,(r2)4 (0023r1r2)
34MULRM16mul16 r1,(r2)4 (0024r1r2)
35MULRM32mul32 r1,(r2)4 (0025r1r2)
36MULMR32mul32 (r1),r24 (0026r1r2)

 減算系のコードでは、演算を実行してその結果を格納すると同時にSigフラグ(結果がマイナス時にセット)とZeroフラグ(結果が0の時にセット)を設定します。

  case SUBRR32:

      u32Wrk1=getRegister((u32Code & 0xff00) >> 8);
      u32Wrk2=getRegister(u32Code & 0xff);

      setSRSig(u32Wrk1<u32Wrk2);
      setSRZero(u32Wrk1==u32Wrk2);

      setRegister((u32Code & 0xff00) >> 8,u32Wrk1-u32Wrk2);

      u32Addr=u32Arg+4;

      break;

 乗算系のコードでは、OFフラグ(オーバーフロー時にセット)とZeroフラグを設定します。関数u32MulOF()は、渡された2つの数値の乗算結果が32ビットの範囲を越えるか判定するものです。

  case MULRR32:

      u32Wrk1=getRegister((u32Code & 0xff00) >> 8);
      u32Wrk2=getRegister(u32Code & 0xff);

      if (u32Wrk2>0)
          setSROF(u32MulOF(u32Wrk1,u32Wrk2));
      else
          setSROF(false);

      u32Wrk=u32Wrk1*u32Wrk2;

      setRegister((u32Code & 0xff00) >> 8,u32Wrk);
      setSRZero(u32Wrk==0);

      u32Addr=u32Arg+4;

      break;

比較・ジャンプ系コード

 比較系コードは、数値の比較を行い等しければZeroフラグをセットし、結果がマイナスになればSigフラグをセットするcpを実装します。これは

 第一オペラント−第二オペラント

 という演算を行い、その結果を格納せずにフラグだけを変更することに相当します。

コード値定数ニーモックコードサイズ
70CPRR8cp8 r1,r14 (0070r1r2)
71CPRR16cp16 r1,r14 (0071r1r2)
72CPRR32cp32 r1,r14 (0072r1r2)
73CPRI8cp8 r1,Imm84 (0073r1Imm8)
74CPRI32cp32 r1,Imm328 (0074r100,Imm32)
75CPRM8cp8 r1,(r2)4 (0075r1r2)
76CPRM16cp16 r1,(r2)4 (0075r1r2)
77CPRM32cp32 r1,(r2)4 (0075r1r2)

 8・16ビットの比較は、「比較対照(2番目のオペラント)の下位8・16ビットとレジスタの全ビットで」比較します。1番目のオペラントで指定されたレジスタは32ビットのまま比較されますので(つまり、「下位8・16ビット同士」の比較ではない)注意してください。

 例えば、レジスタ同士の比較を8ビットで行うCPRR8は以下のような実装になります。

  case CPRR8:

      u32Wrk1=getRegister((u32Code & 0xff00) >> 8);
      u32Wrk2=getRegister((u32Code & 0xff) & 0xff);

      setSRSig(u32Wrk1<u32Wrk2);
      setSRZero(u32Wrk1==u32Wrk2);

      u32Addr=u32Arg+4;

      break;

 一方のレジスタは、32ビットの値そのまま(u32Wrk1)、もう一方は下位8ビット(u32Wrk2)の値を取り出し、u32Wrk1u32Wrk2を比較していますね。メモリについても同様で、r1レジスタの値とr2レジスタの指すメモリの値を8ビットで比較するCPRM8は以下のようになります。

  case CPRM8:

      u32Wrk1=getRegister((u32Code & 0xff00) >> 8);
      u32Wrk2=pmMemory->read8(getRegister(u32Code & 0xff));

      setSRSig(u32Wrk1<u32Wrk2);
      setSRZero(u32Wrk1==u32Wrk2);

      u32Addr=u32Arg+4;

      break;

 ジャンプコードは、前回指定の即値アドレスにジャンプするコードを実装しましたが、少し整理して以下のようにしましょう。

コード値定数ニーモックコードサイズ
80JUMPI32jump Imm328 (00800000,Imm32)
81JUMPR32jump r14 (0080r100)
82JUMPRR32jump r1,r24 (0080r1r2)
83JUMPM32jump (r1)4 (0080r100)
84JPZEI32jpze Imm328 (00800000,Imm32)
85JPZER32jpze r14 (0080r100)
86JPZERR32jpze r1,r24 (0080r1r2)
87JPZEM32jpze (r1)4 (0080r100)

 ジャンプ先の指定は32ビットで行いますが、その指定を即値、レジスタ、レジスタ+レジスタ、メモリの内容、で行うわけですね。表のjumpが単純ジャンプで、jpzeZeroフラグがセットされているときにジャンプする条件ジャンプです。

 jpze系コードの実装はごく簡単で、Zeroフラグがセットされていれば指定アドレスに、セットされていなければコードの後のアドレスにPCをセットするだけです。例えば、JPZERR32は以下のようになります。

  case JPZERR32:

      if (getSRZero()) {

          u32Wrk1=getRegister((u32Code & 0xff00) >> 8);
          u32Wrk2=getRegister(u32Code & 0xff);

          u32Addr=u32Wrk1+u32Wrk2;

      } else
          u32Addr=u32Arg+4;

      break;

プログラム

 今回は、乗算・比較・条件ジャンプコードを使用して、以下のような二重ループの中で8×4ピクセルの長方形を描くプログラムを組んでみました。

  0000  ld32 r0,0x00ff0000
  0008  ld8 r1,128
  000c  ld32 r2,129+512*56+56
  0014  ld8 r3,0
  0018  ld8 r4,0
  001c  ld32 r5,r2
  0020  ld32 r6,r4
  0024  mul8 r6,4
  0028  add32 r5,r6
  002c  ld32 r6,r3
  0030  mul32 r6,512
  0038  add32 r5,r6
  003c  ld32 (r5),r0
  0040  ld8 (r1),1
  0044  cp8 r4,7
  0048  jze 005c
  0050  add8 r4,1
  0054  jump 0x001c
  005c  cp8 r3,3
  0060  jze 0074
  0068  add8 r3,1
  006c  jump 0018

 このアセンブリコードは、Cで書けば以下のような感じになります。

  /* ピクセルとして書き込む値 */
  r0=0x00ff0000;
  /* ビデオシステムI/Oアドレス */
  r1=128
  /* 長方形の先頭アドレス */
  r2=129+512*56+56

  for (r3=0;r3<=3;r3++)
    for (r4=0;r4<=7;r4++) {

      r5=r2+r4*4+r3*512;
      memory[r5]=r0;

      memory[r1]=1;

    }

 ループ内では、長方形の各ピクセルのアドレスを算出し、そこにr0で指定された色の値を書き込んでから、ビデオシステムのI/Oアドレスに1を書き込んで表示する処理を行っています。アセンブリのアドレス計算は、一度レジスタに値を入れてから乗算したりしているのでかなり煩雑ですね。この辺りは、「変数による演算」を簡単に行える高級言語(Cは、高級アセンブラ(^^;と言われることも多いが)のありがたみを実感させられる部分でしょう。
 ループの終了判定は、ループの末端でレジスタに1ずつ値を足していってそのレジスタの値を予め決めておいたループ回数とcpし、ゼロならjpzeでループを抜けることで行っています。

 進行ボタンをクリックし続けると、画面上の表示領域の中央部に長方形が描かれていきますので、レジスタの値を確認しながらコードを実行していってみましょう。

プログラムダウンロード(X2COM03.ZIP/27KB)
戻る トップへ