目次

アセンブラ(AWK)

 自作マイクロコントローラのアセンブリ言語を
 定義し、アセンブラで1と0の組合わせに変換
 します。

 アセンブラの本質は、ニモニックコードを1と0の
 組合わせに変換するだけです。文字列を数値に変換
 することができれば、スクリプト言語を利用しても
 問題ないと考えました。

 ニモニックとプログラミングモデルを定義します。

 ニモニック
  ニモニックは、次のように決めました。

 プログラミングモデル
  プログラミングモデルは、5種に分割して決めます。

   a レジスタ構成
   b メモリアクセス
   c バスアーキテクチャ
   d I/Oアクセス
   e 例外処理

  個々の内容を詰めます。

   a レジスタ構成
      Working Registerを中央に配置し、汎用レジスタを
      GR0からGR7の8種、Data PointerレジスタをX、Yの
      2種としておきます。コード割付は、以下。

              0000 Working Register
              0010 Data Pointer Register X
              0011 Data Pointer Register Y
              1000 GR0
              1001 GR1
              1010 GR2
              1011 GR3
              1100 GR4
              1101 GR5
              1110 GR6
              1111 GR7

      レジスタのサイズは、16ビットとします。

   b メモリアクセス
      メモリは、データとプログラムの2種を用意。

      データメモリのアクセスサイズは、バイト単位
      プログラムメモリのアクセスサイズは、ワード
      単位とします。

      I/Oはデータメモリに割付けます。

      データメモリは、128kバイトとして
      前半をデータ、後半をI/Oにアサイン。

      プログラムメモリは、64kワードとします。

   c バスアーキテクチャ

      コンピュータの動作を、デジタル回路レベルで
      みると、レジスタからレジスタへのデータ転送
      がひとつの機能として含まれています。

      データ転送時にデータ加工を加えると演算処理に
      なります。レジスタからレジスタへのデータ転送
      するときに、バスを利用します。

      バスは、道路を走っている乗合自動車と同じで
      データという客を乗せます。

      レジスタから見ると、データを送出すときと
      受入れときに使うバスが異なります。
      例えば、次のようにバスを割当てします。
      (下図で、GはGateを表現)



      データを送出すときと受入れときに使うバスが
      異なるようにすると、よいのでレジスタ番号を
      決めて転送元と転送先を指定します。
      この方法で、実現できる命令は、以下。

      MOV レジスタ間のデータ転送
      AND レジスタ同士で論理積を求める
      OR レジスタ同士で論理和を求める
      XOR レジスタ同士で排他的論理和を求める
      ADD レジスタ同士で加算を求める
      SUB レジスタ同士で減算を求める

      レジスタは、4ビットで指定できるので
      命令を4ビットで構成すると次のように
      機械語を定義できます。

                     転送先        転送元
      MOV 0001 レジスタ番号 レジスタ番号 0000
      AND 0001 レジスタ番号 レジスタ番号 0001
      OR  0001 レジスタ番号 レジスタ番号 0010
      XOR 0001 レジスタ番号 レジスタ番号 0011
      ADD 0001 レジスタ番号 レジスタ番号 0100
      ADC 0001 レジスタ番号 レジスタ番号 0101
      SUB 0001 レジスタ番号 レジスタ番号 0110
      SBC 0001 レジスタ番号 レジスタ番号 0111

      基本はMOV命令で、演算が必要ならば、その種別を
      最後の4ビットで選択。

      元々なかったキャリーを含めた加減算を入れる
      ことができました。

      命令を4ビットに限定すると、レジスタに値を
      格納する場合の機械語も定義できます。

      MVI レジスタに値を設定

                     転送先        値
      MVI 0000 レジスタ番号 ???? ????

      回転、シフトは次のように定義できます。

      RL レジスタの左回転、回転数はレジスタに入れる
      RR レジスタの右回転、回転数はレジスタに入れる
      SLL レジスタの左論理シフト、シフト数はレジスタに入れる
      SAL レジスタの左算術シフト、シフト数はレジスタに入れる
      SLR レジスタの右論理シフト、シフト数はレジスタに入れる
      SAR レジスタの右算術シフト、シフト数はレジスタに入れる

                     対象レジスタ パラメータ指定 回転方向
      RL 0010 レジスタ番号 レジスタ番号   0010
      RR 0010 レジスタ番号 レジスタ番号   0001

                     対象レジスタ パラメータ指定 シフト方向
      SLL 0010 レジスタ番号 レジスタ番号   0110
      SAL 0010 レジスタ番号 レジスタ番号   0101
      SLR 0010 レジスタ番号 レジスタ番号   1010
      SAR 0010 レジスタ番号 レジスタ番号   1101

      同じようにバスを使った処理で、INC、DECの機械語を定義できます。

      INC Working Register、汎用レジスタ、Data Pointerの値を+1
      DEC Working Register、汎用レジスタ、Data Pointerの値を−1

                     対象レジスタ      操作
      INC 0010 レジスタ番号 0111 1110
      DEC 0010 レジスタ番号 0111 1111

      存在しないレジスタ番号を使い、おかしな動作を
      しないようにします。

      メモリと情報交換するには、LD、ST命令を使います。
      Data Pointerレジスタの中にある値をアドレスとして
      使い、Workingレジスタを対象とします。

      対象レジスタは固定で、Data PointerレジスタはX、Yの
      いずれかなのでレジスタ番号を指定。LDかSTを操作ビット
      決めます。

      レジスタは16ビットと決めたので、Data Pointerレジスタ
      ひとつで、64ワードのアドレスを指定できます。

      LD  データメモリからWorkingレジスタにデータ転送
      ST  Workingレジスタからデータメモリにデータ転送

                    レジスタ          操作
      LD 0011 レジスタ番号 0000 0000
      ST 0011 レジスタ番号 0000 0001

      分岐命令の場合、プログラムカウンタ値の操作なので
      フラグを見て処理を決めます。

      フラグをレジスタとして扱い、プログラムカウンタに
      指定アドレス値を格納します。

      フラグは、N、Z、Cの3ビットなので、命令コードの
      次の2ビットで、どのフラグかを指定します。

      JNG レジスタのMSBが1のとき、指定ステップに分岐
      JZE ゼロフラグが1のとき、指定ステップに分岐
      JNC キャリーフラグが0のとき、指定ステップに分岐
      JMP 指定ステップに分岐

                     フラグ アドレス
      JNG 0100   01    ?? ???? ????
      JZE 0100   10    ?? ???? ????
      JNC 0100   11    ?? ???? ????
      JMP 0100   00    ?? ???? ????

      アドレスに10ビットを確保するので、0から1023までの
      アドレスをつけられます。

   d I/Oアクセス
     コンピュータのI/Oは、メモリ空間に配置するか、I/O専用の
     アドレスを用意するかのどちらかになります。

     Data Pointerレジスタを利用すれば、64ワードのアドレス
     を自由にアクセスできるので、メモリ空間にI/Oがあると
     する方式を採用。

   e 例外処理
     コンピュータは、例外処理をして緊急性の高い事象に
     対して反応することが要求されることがあります。
     これを割込みというメカニズムで対応するのが、一般的
     ですが、今回のコンピュータは、例外処理を扱いません。

     「例外処理を扱わない!」2つのコンピュータを使えば
     大抵の割込みに対応できるので、複雑になることが
     わかっているメカニズムを備えることはしません。

 アセンブリ言語の仕様を決めたので、擬似命令に
 関しての検討を始めます。

 擬似命令は、ラベルに数値や文字列の置換をさせる他に
 数式の処理も許す処理系があるので、仕様を確定させて
 から、コードを作成に移ります。

 擬似命令は、ラベルを数値で置き換える「EQU」と
 アセンブル作業終了の「END」だけを使う仕様です。

 命令をグループごとにAWKのスクリプトにしていきます。

 転送

  転送命令では、レジスタ番号が2つ必要なので
  16ビット中の最初と最後のニブルで分類後に
  レジスタ番号を追加します。

  レジスタ番号は、以下。

    0000 Working Register
    0010 Data Pointer Register X
    0011 Data Pointer Register Y
    1000 GR0
    1001 GR1
    1010 GR2
    1011 GR3
    1100 GR4
    1101 GR5
    1110 GR6
    1111 GR7

  命令と各ニブルの組合せを確認。

   MOV 0001 レジスタ番号 レジスタ番号 0000
   AND 0001 レジスタ番号 レジスタ番号 0001
   OR  0001 レジスタ番号 レジスタ番号 0010
   XOR 0001 レジスタ番号 レジスタ番号 0011
   ADD 0001 レジスタ番号 レジスタ番号 0100
   ADC 0001 レジスタ番号 レジスタ番号 0101
   SUB 0001 レジスタ番号 レジスタ番号 0110
   SBC 0001 レジスタ番号 レジスタ番号 0111

  1命令ごとに、必要な2進数を指定します。

      if ( xopc == "MOV" || xopc == "AND" ||
           xopc == "OR"  || xopc == "XOR" ||
           xopc == "ADD" || xopc == "ADC" ||
           xopc == "SUB" || xopc == "SBC"    ) {
        xcode0 = "0001"
        xcode1 = ""
        xcode2 = ""
        if ( xcar == "WR" )  { xcode1 = "0000" }
        if ( xcdr == "WR" )  { xcode2 = "0000" }
        if ( xcar == "DX" )  { xcode1 = "0001" }
        if ( xcdr == "DX" )  { xcode2 = "0001" }
        if ( xcar == "DY" )  { xcode1 = "0010" }
        if ( xcdr == "DY" )  { xcode2 = "0010" }
        if ( xcar == "GR0" ) { xcode1 = "1000" }
        if ( xcdr == "GR0" ) { xcode2 = "1000" }
        if ( xcar == "GR1" ) { xcode1 = "1001" }
        if ( xcdr == "GR1" ) { xcode2 = "1001" }
        if ( xcar == "GR2" ) { xcode1 = "1010" }
        if ( xcdr == "GR2" ) { xcode2 = "1010" }
        if ( xcar == "GR3" ) { xcode1 = "1011" }
        if ( xcdr == "GR3" ) { xcode2 = "1011" }
        if ( xcar == "GR4" ) { xcode1 = "1100" }
        if ( xcdr == "GR4" ) { xcode2 = "1100" }
        if ( xcar == "GR5" ) { xcode1 = "1101" }
        if ( xcdr == "GR5" ) { xcode2 = "1101" }
        if ( xcar == "GR6" ) { xcode1 = "1110" }
        if ( xcdr == "GR6" ) { xcode2 = "1110" }
        if ( xcar == "GR7" ) { xcode1 = "1111" }
        if ( xcdr == "GR7" ) { xcode2 = "1111" }
      }
      if ( xopc == "MOV" ) { xcode3 = "0000" }
      if ( xopc == "AND" ) { xcode3 = "0001" }
      if ( xopc == "OR"  ) { xcode3 = "0010" }
      if ( xopc == "XOR" ) { xcode3 = "0011" }
      if ( xopc == "ADD" ) { xcode3 = "0100" }
      if ( xopc == "ADC" ) { xcode3 = "0101" }
      if ( xopc == "SUB" ) { xcode3 = "0110" }
      if ( xopc == "SBC" ) { xcode3 = "0111" }

 設定

  レジスタに値を設定するには、レジスタ番号と
  値を指定すればよいので、単純です。
  以下のようにニブルを並べます。

   MVI 0000 レジスタ番号 ???? ????

  コードは、以下。

      if ( xopc == "MVI" ) {
        xcode0 = "0000"
        if ( xcar == "WR" )  { xcode1 = "0000" }
        if ( xcdr == "DX" )  { xcode1 = "0001" }
        if ( xcar == "DY" )  { xcode1 = "0010" }
        if ( xcar == "GR0" ) { xcode1 = "1000" }
        if ( xcar == "GR1" ) { xcode1 = "1001" }
        if ( xcar == "GR2" ) { xcode1 = "1010" }
        if ( xcar == "GR3" ) { xcode1 = "1011" }
        if ( xcar == "GR4" ) { xcode1 = "1100" }
        if ( xcar == "GR5" ) { xcode1 = "1101" }
        if ( xcar == "GR6" ) { xcode1 = "1110" }
        if ( xcar == "GR7" ) { xcode1 = "1111" }
        # convert
        xcode2 = conv_binary(xcar / 16)
        xcode3 = conv_binary(xcar % 16)
      }

 回転、シフト

  2レジスタで対象と回転数、シフト数を指定するので
  レジスタ番号を利用します。

  2番目のレジスタに回転数、シフト数を格納。
  ニブル設定は、次のようになっています。

   RL 0010 レジスタ番号 レジスタ番号 0010
   RR 0010 レジスタ番号 レジスタ番号 0001
   SLL 0010 レジスタ番号 レジスタ番号 0110
   SAL 0010 レジスタ番号 レジスタ番号 0101
   SLR 0010 レジスタ番号 レジスタ番号 1010
   SAR 0010 レジスタ番号 レジスタ番号 1101

  コードは、以下。

      if ( xopc == "RL"  || xopc == "RR"  ||
           xopc == "SLL" || xopc == "SAL" ||
           xopc == "SLR" || xopc == "SAR"    ) {
        xcode0 = "0010"
        xcode1 = ""
        xcode2 = ""
        if ( xcar == "WR" )  { xcode1 = "0000" }
        if ( xcdr == "WR" )  { xcode2 = "0000" }
        if ( xcar == "DX" )  { xcode1 = "0001" }
        if ( xcdr == "DX" )  { xcode2 = "0001" }
        if ( xcar == "DY" )  { xcode1 = "0010" }
        if ( xcdr == "DY" )  { xcode2 = "0010" }
        if ( xcar == "GR0" ) { xcode1 = "1000" }
        if ( xcdr == "GR0" ) { xcode2 = "1000" }
        if ( xcar == "GR1" ) { xcode1 = "1001" }
        if ( xcdr == "GR1" ) { xcode2 = "1001" }
        if ( xcar == "GR2" ) { xcode1 = "1010" }
        if ( xcdr == "GR2" ) { xcode2 = "1010" }
        if ( xcar == "GR3" ) { xcode1 = "1011" }
        if ( xcdr == "GR3" ) { xcode2 = "1011" }
        if ( xcar == "GR4" ) { xcode1 = "1100" }
        if ( xcdr == "GR4" ) { xcode2 = "1100" }
        if ( xcar == "GR5" ) { xcode1 = "1101" }
        if ( xcdr == "GR5" ) { xcode2 = "1101" }
        if ( xcar == "GR6" ) { xcode1 = "1110" }
        if ( xcdr == "GR6" ) { xcode2 = "1110" }
        if ( xcar == "GR7" ) { xcode1 = "1111" }
        if ( xcdr == "GR7" ) { xcode2 = "1111" }
      }
      if ( xopc == "RL"  ) { xcode3 = "0010" }
      if ( xopc == "RR"  ) { xcode3 = "0001" }
      if ( xopc == "SLL" ) { xcode3 = "0110" }
      if ( xopc == "SAL" ) { xcode3 = "0101" }
      if ( xopc == "SLR" ) { xcode3 = "1010" }
      if ( xopc == "SAR" ) { xcode3 = "1010" }

 レジスタ増減

  レジスタは1つでよいので、増減だけを指定します。
  レジスタ番号を利用します。

  ニブル設定は、次のようにします。

   INC 0010 レジスタ番号 0111 1110
   DEC 0010 レジスタ番号 0111 1111

  コードは、以下。

      if ( xopc == "INC" || xopc == "DEC ) {
        xcode0 = "0010"
        xcode1 = ""
        xcode2 = "0111"
        if ( xcar == "WR" )  { xcode1 = "0000" }
        if ( xcar == "DX" )  { xcode1 = "0001" }
        if ( xcar == "DY" )  { xcode1 = "0010" }
        if ( xcar == "GR0" ) { xcode1 = "1000" }
        if ( xcar == "GR1" ) { xcode1 = "1001" }
        if ( xcar == "GR2" ) { xcode1 = "1010" }
        if ( xcar == "GR3" ) { xcode1 = "1011" }
        if ( xcar == "GR4" ) { xcode1 = "1100" }
        if ( xcar == "GR5" ) { xcode1 = "1101" }
        if ( xcar == "GR6" ) { xcode1 = "1110" }
        if ( xcar == "GR7" ) { xcode1 = "1111" }
        xcode3 = "1110"
        if ( xopc == "DEC" ) { xcode3 = "1111" }
      }

 メモリ転送

  メモリとデータ転送するのは、Workingレジスタだけに
  限定するので、データポインタのどちらを利用するかを
  レジスタ番号で指定します。

  ニブル設定は、次のようにします。

   LD 0011 レジスタ番号 0000 0000
   ST 0011 レジスタ番号 0000 0001

  コードは、以下。

      if ( xopc == "LD"  || xopc == "ST" ) {
        xcode0 = "0011"
        xcode1 = ""
        if ( xcar == "DX" )  { xcode1 = "0001" }
        if ( xcar == "DY" )  { xcode1 = "0010" }
        xcode2 = "0000"
        xcode3 = "0000"
        if ( xopc == "ST"  ) { xcode3 = "0001" }
      }

 分岐

  分岐は、プログラムカウンタ値の操作なので
  フラグを見て処理を決めます。
  フラグをレジスタとして扱い、プログラムカウンタに
  指定アドレス値を格納します。

  フラグは、N、Z、Cの3ビットなので、命令コードの
  次の2ビットで、どのフラグかを指定します。

  フラグとの組合せを次のように指定。

  JNG レジスタのMSBが1のとき、指定ステップに分岐
  JZE ゼロフラグが1のとき、指定ステップに分岐
  JNC キャリーフラグが0のとき、指定ステップに分岐
  JMP 指定ステップに分岐

  ニブル設定は、次のようにします。

               フラグ アドレス
   JNG 0100   01    ?? ???? ????
   JZE 0100   10    ?? ???? ????
   JNC 0100   11    ?? ???? ????
   JMP 0100   00    ?? ???? ????

  コードは、以下。

      if ( xopc == "JNG" || xopc == "JZE" ) {
           xopc == "JNC" || xopc == "JMP" ) {
        xcode0 = "0100"
        xcode1 = conv_binary((xcdr / 256) % 4)
        xcode2 = conv_binary((xcdr / 16) % 16)
        xcode3 = conv_binary(xcdr % 16)
      }
      if ( xopc == "JNG"  ) { xcode1 = "01" xcode1 }
      if ( xopc == "JZE"  ) { xcode1 = "10" xcode1 }
      if ( xopc == "JNC"  ) { xcode1 = "11" xcode1 }
      if ( xopc == "JMP"  ) { xcode1 = "00" xcode1 }

 利用している関数conv_binaryを定義します。

function conv_binary(x)
{
  # default
  result = "0000"
  # judge and set
  if ( x == 1 )  { result = "0001" }
  if ( x == 2 )  { result = "0010" }
  if ( x == 3 )  { result = "0011" }
  if ( x == 4 )  { result = "0100" }
  if ( x == 5 )  { result = "0101" }
  if ( x == 6 )  { result = "0110" }
  if ( x == 7 )  { result = "0111" }
  if ( x == 8 )  { result = "1000" }
  if ( x == 9 )  { result = "1001" }
  if ( x == 10 ) { result = "1010" }
  if ( x == 11 ) { result = "1011" }
  if ( x == 12 ) { result = "1100" }
  if ( x == 13 ) { result = "1101" }
  if ( x == 14 ) { result = "1110" }
  if ( x == 15 ) { result = "1111" }

  return result
}

(under construction)

目次

inserted by FC2 system