目次
前
次
インタプリタ作成
命令コードの仕様を確定し、コンバータを作成したので
スキャン処理のため、インタプリタを作成します。
インタプリタの動作シーケンスを考えます。
回路情報は、EEPROM内に格納されているので1ワード
ごとに取得し、解釈し、結果をメモリに保存するのが
基本動作になります。
この動作をシーケンスで記述してみます。
- ROMアドレスを0に設定
- ROMから、1ワード(=16bits)データを取得
- 取得データが、すべて0ならば動作を終了
- 命令コード、ビットアドレスに分割
- ビットアドレスに従い、指定された1ビット取得
- 命令コードに従い、1ビットデータを生成
- 生成した1ビットデータを、メモリに保存
- ROMアドレスインクリメント
- 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命令の内容を、もう一度見てみます。
- LD A接点データを入力し、レジスタに保存
- LDN B接点データを入力し、レジスタに保存
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ビットデータを生成するには、命令ごとに
どういう処理をするかを決定するだけです。
命令の一覧を眺めてみます。
- END : 0000 0000 0000 0000
- OUT : 0001 0000 bbbb bbbb
- LD : 0010 0000 bbbb bbbb
- LDN : 0011 0000 bbbb bbbb
- AND : 0100 0000 bbbb bbbb
- ANDN : 0101 0000 bbbb bbbb
- OR : 0110 0000 bbbb bbbb
- ORN : 0111 0000 bbbb bbbb
- ANDB : 1000 0000 0000 0000
- ORB : 1001 0000 0000 0000
- SETC : 1010 bbbb ???? ????
- INC : 1011 bbbb 0000 0000
- DEC : 1100 bbbb 0000 0000
- SETT : 1101 bbbb ???? ????
- COND : 1110 bbbb ???? ????
- JUMP : 1111 00?? ???? ????
END命令は、forループを脱出するために使って
いるので、1ビット生成処理からは除外できます。
LD命令とLDN命令は、対象データ取得で定義済みです。
残りは、OUT命令と論理演算命令です。
OUTは、レジスタに入っている結果を、配列memoryの
指定ビットに転送します。従って、データ生成処理
では扱いません。
論理演算だけがデータ生成に関与します。
次の動作から、各論理演算を記述します。
- AND A接点データとの論理積を求めレジスタに保存
- ANDN B接点データとの論理積を求めレジスタに保存
- OR A接点データとの論理和を求めレジスタに保存
- ORN B接点データとの論理和を求めレジスタに保存
- ANDB ブロック間の論理積を求めレジスタに保存
- ORB ブロック間の論理和を求めレジスタに保存
論理和、論理積は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 ;
}
目次
前
次