目次

インタプリタ作成

 命令コードの仕様を確定し、コンバータを作成したので
 スキャン処理のため、インタプリタを作成します。

 インタプリタの動作シーケンスを考えます。

 回路情報は、EEPROM内に格納されているので1ワード
 ごとに取得し、解釈し、結果をメモリに保存するのが
 基本動作になります。

 この動作をシーケンスで記述してみます。
  1. ROMアドレスを0に設定
  2. ROMから、1ワード(=16bits)データを取得
  3. 取得データが、すべて0ならば動作を終了
  4. 命令コード、ビットアドレスに分割
  5. ビットアドレスに従い、指定された1ビット取得
  6. 命令コードに従い、1ビットデータを生成
  7. 生成した1ビットデータを、メモリに保存
  8. ROMアドレスインクリメント
  9. 2にもどる
 ラダー図の最大行番号は、1023なのでfor文で  繰返しを記述します。 int i ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ /* 取得データが、すべて0ならば動作を終了 */ /* 命令コード、ビットアドレスに分割 */ /* ビットアドレスに従い、指定された1ビット取得 */ /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ } ROMアドレスは、0〜2047になるので、16ビット  サイズで指定します。 typedef unsigned short UWORD ; int i ; UWORD rom_address ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ /* 取得データが、すべて0ならば動作を終了 */ /* 命令コード、ビットアドレスに分割 */ /* ビットアドレスに従い、指定された1ビット取得 */ /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ } 1行の回路動作を情報として取得します。  回路情報が0ならば、END命令なので、forループ  を抜けます。 typedef unsigned short UWORD ; int i ; UWORD rom_address ; UWORD operand ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 取得データが、すべて0ならば動作を終了 */ if ( operand == 0 ) break ; /* 命令コード、ビットアドレスに分割 */ /* ビットアドレスに従い、指定された1ビット取得 */ /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ } 1行の回路動作を取得すると、次の動作は  ROMアドレスに+2を加えた位置にあります。  ROMアドレスの更新処理を加えます。 typedef unsigned short UWORD ; int i ; UWORD rom_address ; UWORD operand ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 取得データが、すべて0ならば動作を終了 */ if ( operand == 0 ) break ; /* 命令コード、ビットアドレスに分割 */ /* ビットアドレスに従い、指定された1ビット取得 */ /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ rom_address += 2 ; }  シーケンスに沿って、動作をより詳しく検討します。

回路情報取得

回路情報取得は、関数get_circuit_infomationの担当です。  ROMアドレスをパラメータとして受取り、2バイトデータ を返せば、目的は達成できます。  関数の体裁を整えます。 typdef unsigned short UWORD ; UWORD get_circuit_infomation(UWORD x) { return 0 ; }  マイコンの関数ライブラリに、EEPROMから1バイトを  取得する関数read_eepromが用意されていると仮定し  内部を定義します。 typdef unsigned short UWORD ; typdef unsigned char UBYTE ; UWORD get_circuit_infomation(UWORD x) { UBYTE dh; UBYTE dl; UWORD result ; /* get upper byte */ dh = read_eeprom( x ) ; /* get lower byte */ dl = read_eeprom( x+1 ) ; /* concatenate */ result = dh ; result <<= 8 ; result |= dl ; return result ; }

情報分離

回路情報は、operandに格納されています。  16ビットのうち、命令は上位4ビットに保存されて  いるので、シフト命令で4ビットだけ取り出します。 opcode = (UBYTE)(operand >> 12);  マスク処理で、残り12ビットを、operandに残します。 operand &= 0x0fff ;  情報分離を、インタプリタ処理に加えます。 typedef unsigned short UWORD ; int i ; UWORD rom_address ; UWORD operand ; UBYTE opcode ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 取得データが、すべて0ならば動作を終了 */ if ( operand == 0 ) break ; /* 命令コード、ビットアドレスに分割 */ opcode = (UBYTE)(operand >> 12); operand &= 0x0fff ; /* ビットアドレスに従い、指定された1ビット取得 */ /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ rom_address += 2 ; }

対象データ取得

対象とする1ビットデータを取得する処理は、LD命令か  LDN命令を実行していることになります。  対象となる1ビットデータは、配列memoryの中にあります。 LD、LDN命令の内容を、もう一度見てみます。  A接点データは1で意味をもち、B接点データは0  で意味をもちます。  A接点は正論理で、B接点は負論理で考えなくては  なりません。動作を考えるとき、2つの論理がある  と、処理が面倒なので、正論理に統一して考えます。  まず、指定されたビットアドレスを利用して、1ビット  データを取得します。  変数operandの下位8ビットにビットアドレスがあります。  このビットアドレスを利用して、対象とする1ビットの  データを取り出します。  ビットアドレスは、0〜255なので、1バイトごとに  データを保存している配列memoryのどこに、対象の  ビットが存在するかを計算します。 q = (operand & 0xff) / 8 ;  2のベキ乗で割って商を求める場合、右にシフト  した方が、処理時間が短くなることが多いので、  シフト命令で置き換えます。 q = ((operand & 0xff) >> 3);  対象ビットが入っているメモリのバイト位置が確定  したので、1バイトを取り出します。 reg = *(memory+q);  ビット位置を計算します。ビットアドレスを8で割った余りを  求めると、ビット位置になります。ビット位置は0〜7と  なるので、ビットアドレスの下位3ビットをマスクすると計算  できます。 r = ((operand & 0xff) & 0x07);  ビット位置が確定したので、変数regに保存してある1バイト  をシフトして、LSBに対象ビットを移します。 reg = (reg >> r) & 1;  正論理で考えるので、命令を判断してビットの値を  決定します。 #define OP_END 0 #define OP_OUT 1 #define OP_LD 2 #define OP_LDN 3 #define OP_AND 4 #define OP_ANDN 5 #define OP_OR 6 #define OP_ORN 7 #define OP_ANDB 8 #define OP_ORB 9 #define OP_SETC 10 #define OP_INC 11 #define OP_DEC 12 #define OP_SETT 13 #define OP_COND 14 #define OP_JUMP 15 if ( operand == OP_LD || operand == OP_LDN ) { /* ビットアドレスに従い、指定された1ビット取得 */ q = ((operand & 0xff) >> 3); reg = *(memory+q); r = ((operand & 0xff) & 0x07); reg = (reg >> r) & 1; if ( operand == OP_LDN ) { reg ^= 1 ; } }  対象データ取得を、インタプリタ処理に加えます。 typedef unsigned short UWORD ; #define OP_END 0 #define OP_OUT 1 #define OP_LD 2 #define OP_LDN 3 #define OP_AND 4 #define OP_ANDN 5 #define OP_OR 6 #define OP_ORN 7 #define OP_ANDB 8 #define OP_ORB 9 #define OP_SETC 10 #define OP_INC 11 #define OP_DEC 12 #define OP_SETT 13 #define OP_COND 14 #define OP_JUMP 15 int i ; UWORD rom_address ; UWORD operand ; UBYTE opcode ; UBYTE q ; UBYTE r ; UBYTE reg ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 取得データが、すべて0ならば動作を終了 */ if ( operand == 0 ) break ; /* 命令コード、ビットアドレスに分割 */ opcode = (operand >> 12); operand &= 0x0fff ; /* LD LDN 命令 */ if ( opcode == OP_LD || opcode == OP_LDN ) { q = ((operand & 0xff) >> 3); r = ((operand & 0xff) & 0x07); reg = *(memory+q); reg = (reg >> r) & 1; if ( operand == OP_LDN ) { reg ^= 1 ; } } /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ rom_address += 2 ; }  命令コードを取得すると、forループを終了するか  どうか判定できます。  forループ終了条件を加えます。 typedef unsigned short UWORD ; #define OP_END 0 #define OP_OUT 1 #define OP_LD 2 #define OP_LDN 3 #define OP_AND 4 #define OP_ANDN 5 #define OP_OR 6 #define OP_ORN 7 #define OP_ANDB 8 #define OP_ORB 9 #define OP_SETC 10 #define OP_INC 11 #define OP_DEC 12 #define OP_SETT 13 #define OP_COND 14 #define OP_JUMP 15 int i ; UWORD rom_address ; UWORD operand ; UBYTE opcode ; UBYTE q ; UBYTE r ; UBYTE reg ; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 命令コード、ビットアドレスに分割 */ opcode = (operand >> 12); operand &= 0x0fff ; /* END 命令 */ if ( operand == OP_END ) break ; /* LD LDN 命令 */ if ( opcode == OP_LD || opcode == OP_LDN ) { q = ((operand & 0xff) >> 3); r = ((operand & 0xff) & 0x07); reg = *(memory+q); reg = (reg >> r) & 1; if ( operand == OP_LDN ) { reg ^= 1 ; } } /* 命令コードに従い、1ビットデータを生成 */ /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ rom_address += 2 ; }

データ生成

1ビットデータを生成するには、命令ごとに  どういう処理をするかを決定するだけです。  命令の一覧を眺めてみます。
  1. END : 0000 0000 0000 0000
  2. OUT : 0001 0000 bbbb bbbb
  3. LD : 0010 0000 bbbb bbbb
  4. LDN : 0011 0000 bbbb bbbb
  5. AND : 0100 0000 bbbb bbbb
  6. ANDN : 0101 0000 bbbb bbbb
  7. OR : 0110 0000 bbbb bbbb
  8. ORN : 0111 0000 bbbb bbbb
  9. ANDB : 1000 0000 0000 0000
  10. ORB : 1001 0000 0000 0000
  11. SETC : 1010 bbbb ???? ????
  12. INC : 1011 bbbb 0000 0000
  13. DEC : 1100 bbbb 0000 0000
  14. SETT : 1101 bbbb ???? ????
  15. COND : 1110 bbbb ???? ????
  16. JUMP : 1111 00?? ???? ????
 END命令は、forループを脱出するために使って  いるので、1ビット生成処理からは除外できます。  LD命令とLDN命令は、対象データ取得で定義済みです。  残りは、OUT命令と論理演算命令です。  OUTは、レジスタに入っている結果を、配列memoryの  指定ビットに転送します。従って、データ生成処理  では扱いません。  論理演算だけがデータ生成に関与します。  次の動作から、各論理演算を記述します。  論理和、論理積は2項演算なので、変数regのデータ  の他に、もう一つデータが必要になります。  もう一つのデータを取出す記述を加えます。 q = ((operand & 0xff) >> 3); r = ((operand & 0xff) & 0x07); if ( opcode == OP_AND || opcode == OP_ANDN || opcode == OP_OR || opcode == OP_ORN ) { regx = *(memory+q); regx = (regx >> r) & 1; /* 反転 */ if ( opcode == OP_ANDN || opcode == OP_ORN ) { regx ^= 1 ; } /* 論理演算 */ }  論理和、論理積を求めて、レジスタに保存します。 if ( opcode == OP_AND || opcode == OP_ANDN || opcode == OP_OR || opcode == OP_ORN ) { regx = *(memory+q); regx = (regx >> r) & 1; /* 反転 */ if ( opcode == OP_ANDN || opcode == OP_ORN ) { regx ^= 1 ; } /* 論理積 */ if ( opcode == OP_AND || opcode == OP_ANDN ) { reg &= regx ; } /* 論理和 */ if ( opcode == OP_OR || opcode == OP_ORN ) { reg |= regx ; } }  ブロック間の論理演算を考えます。  論理演算なので、2つブロックの論理値が必要です。  具体的なラダー図で検討します。  このラダー図を、命令に展開します。 LD 0 AND 1 LD 3 OR 4 ORB OUT 9  現時点でのインタプリタはどう動作するかを見ます。 LD 0 : ビット番地0の値をregに保存 AND 1 : ビット番地1の値をregxに保存 論理積は、regに保存 LD 3 : ビット番地3の値をregに保存 OR 4 : ビット番地4の値をregxに保存 論理和は、regに保存 ORB : OUT 9 :  1つめの処理で求めた値が、破壊されています。  データ破壊を防ぐためには、ブロック間論理演算  を実行するまで、データを一時保存しておければ  よいでしょう。  データの一時保存には、スタックを使います。  レジスタregの値が、破壊されないようにLD命令、LDN命令  を実行する前に、regの値をスタックに退避します。  スタックの実現方法は、いろいろありますが、1ビット  データを処理するなら、1バイト変数をシフトレジスタ  として使えます。深さは8になります。  スタックとデータをやりとりする命令は、PUSH、PULLと  します(68系アセンブリ言語のニモニックにあります)。  PUSH、PULL命令があるとして、先の命令を記述し  直すしてみます。 LD 0 : regの値をスタックに保存(PUSH)ビット番地0の値をregに保存 AND 1 : ビット番地1の値をregxに保存 論理積はregに保存 LD 3 : regの値をスタックに保存(PUSH)ビット番地3の値をregに保存 OR 4 : ビット番地4の値をregxに保存 論理和はregに保存 ORB : スタックから値を取得しregxに保存(PULL) regとregxの論理和を求め、regに保存 OUT 9 : regの値をビット番地9に保存  PUSHとPULLの利用回数は異なりますが、最初にPUSH  した値は、元々利用する目的でスタックに入れた訳  ではないので、問題ありません。  PUSH、PULLを実現する関数を定義します。 UBYTE stksft ; void push(UBYTE x) { stksft <<= 1 ; stksft &= 0xfe ; stksft |= x ; } UBYTE pull(void) { UBYTE reg ; reg = stksft ; stksft >>= 1 ; return(reg & 1); }  スタック処理関数を定義したので、LD命令、LDN命令の  処理を改造します。 if ( opcode == OP_LD || opcode == OP_LDN ) { push( reg ); reg = *(memory+q); reg = (reg >> r) & 1; if ( operand == OP_LDN ) { reg ^= 1 ; } }  ブロック間論理演算処理を記述します。 if ( opcode == OP_ANDB || opcode == OP_ORB ) { regx = pull(); if ( opcode == OP_ANDB ) { reg &= regx ; } if ( opcode == OP_ORB ) { reg |= regx ; } }  データ生成を、インタプリタ処理に加えます。 typedef unsigned short UWORD ; #define OP_END 0 #define OP_OUT 1 #define OP_LD 2 #define OP_LDN 3 #define OP_AND 4 #define OP_ANDN 5 #define OP_OR 6 #define OP_ORN 7 #define OP_ANDB 8 #define OP_ORB 9 #define OP_SETC 10 #define OP_INC 11 #define OP_DEC 12 #define OP_SETT 13 #define OP_COND 14 #define OP_JUMP 15 int i ; UWORD rom_address ; UWORD operand ; UBYTE opcode ; UBYTE q ; UBYTE r ; UBYTE reg ; UBYTE regx; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 命令コード、ビットアドレスに分割 */ opcode = (operand >> 12); operand &= 0x0fff ; q = ((operand & 0xff) >> 3); r = ((operand & 0xff) & 0x07); /* END 命令 */ if ( opcode == OP_END ) break ; /* LD LDN 命令 */ if ( opcode == OP_LD || opcode == OP_LDN ) { push( reg ); reg = *(memory+q); reg = (reg >> r) & 1; if ( opcode == OP_LDN ) { reg ^= 1 ; } } /* OP_AND OP_ANDN OP_OR OP_ORN 命令 */ if ( opcode == OP_AND || opcode == OP_ANDN || opcode == OP_OR || opcode == OP_ORN ) { regx = *(memory+q); regx = (regx >> r) & 1; /* 反転 */ if ( opcode == ANDN || opcode == OP_ORN ) { regx ^= 1 ; } /* 論理積 */ if ( opcode == OP_AND || opcode == OP_ANDN ) { reg &= regx ; } /* 論理和 */ if ( opcode == OP_OR || opcode == OP_ORN ) { reg |= regx ; } } /* OP_ANDB OP_ORB 命令 */ if ( opcode == OP_ANDB || opcode == OP_ORB ) { regx = pull(); if ( opcode == OP_ANDB ) { reg &= regx ; } if ( opcode == OP_ORB ) { reg |= regx ; } } /* 生成した1ビットデータを、メモリに保存 */ /* ROMアドレスインクリメント */ rom_address += 2 ; }

データ保存

 データ保存は、OUT命令で処理します。  OUT命令は、レジスタの1ビットデータ出力に  なるので、変数regの値を指定ビット番地へと  保存します。  ビット番地は計算済みなので、現在入っている  値を0クリア後、論理和でデータを転送します。 reg <<= r ; *(memory+q) ^= ~(1 << r); *(memory+q) |= reg;  データ保存を、インタプリタ処理に加えます。 typedef unsigned short UWORD ; #define OP_END 0 #define OP_OUT 1 #define OP_LD 2 #define OP_LDN 3 #define OP_AND 4 #define OP_ANDN 5 #define OP_OR 6 #define OP_ORN 7 #define OP_ANDB 8 #define OP_ORB 9 #define OP_SETC 10 #define OP_INC 11 #define OP_DEC 12 #define OP_SETT 13 #define OP_COND 14 #define OP_JUMP 15 int i ; UWORD rom_address ; UWORD operand ; UBYTE opcode ; UBYTE q ; UBYTE r ; UBYTE reg ; UBYTE regx; rom_address = 0 ; for ( i = 0 ; i < 1024 ; i++ ) { /* ROMから、1ワード(=16bits)データを取得 */ operand = get_circuit_infomation( rom_address ); /* 命令コード、ビットアドレスに分割 */ opcode = (operand >> 12); operand &= 0x0fff ; q = ((operand & 0xff) >> 3); r = ((operand & 0xff) & 0x07); /* END 命令 */ if ( opcode == OP_END ) break ; /* LD LDN 命令 */ if ( opcode == OP_LD || opcode == OP_LDN ) { push( reg ); reg = *(memory+q); reg = (regx >> r) & 1; if ( opcode == OP_LDN ) { reg ^= 1 ; } } /* OP_AND OP_ANDN OP_OR OP_ORN 命令 */ if ( opcode == OP_AND || opcode == OP_ANDN || opcode == OP_OR || opcode == OP_ORN ) { regx = *(memory+q); regx = (regx >> r) & 1; /* 反転 */ if ( opcode == OP_ANDN || opcode == OP_ORN ) { regx ^= 1 ; } /* 論理積 */ if ( opcode == OP_AND || opcode == OP_ANDN ) { reg &= regx ; } /* 論理和 */ if ( opcode == OP_OR || opcode == OP_ORN ) { reg |= regx ; } } /* OP_ANDB OP_ORB 命令 */ if ( opcode == OP_ANDB || opcode == OP_ORB ) { regx = pull(); if ( opcode == OP_ANDB ) { reg &= regx ; } if ( opcode == OP_ORB ) { reg |= regx ; } } /* OP_OUT 命令 */ if ( opcode == OP_OUT ) { reg <<= r ; *(memory+q) ^= ~(1 << r); *(memory+q) |= reg; } /* ROMアドレスインクリメント */ rom_address += 2 ; }
目次

inserted by FC2 system