目次
前
次
アセンブラ(AWK)
自作マイクロコントローラのアセンブリ言語を
定義し、アセンブラで1と0の組合わせに変換
します。
アセンブラの本質は、ニモニックコードを1と0の
組合わせに変換するだけです。文字列を数値に変換
することができれば、スクリプト言語を利用しても
問題ないと考えました。
ニモニックとプログラミングモデルを定義します。
ニモニック
ニモニックは、次のように決めました。
- LD データメモリからWorking Registerにデータ転送
- ST Working Registerからデータメモリにデータ転送
- JNG レジスタのMSBが1のとき、指定ステップに分岐
- JZE ゼロフラグが1のとき、指定ステップに分岐
- JNC キャリーフラグが0のとき、指定ステップに分岐
- JMP 指定ステップに分岐
- MVI レジスタに値を設定
- MOV レジスタ間のデータ転送
- AND レジスタ同士で論理積を求める
- OR レジスタ同士で論理和を求める
- XOR レジスタ同士で排他的論理和を求める
- ADD レジスタ同士で加算を求める
- SUB レジスタ同士で減算を求める
- RL レジスタの左回転、回転数はレジスタに入れる
- RR レジスタの右回転、回転数はレジスタに入れる
- SLL レジスタの左論理シフト、シフト数はレジスタに入れる
- SAL レジスタの左算術シフト、シフト数はレジスタに入れる
- SLR レジスタの右論理シフト、シフト数はレジスタに入れる
- SAR レジスタの右算術シフト、シフト数はレジスタに入れる
- EQ Working Registerと値が等しいかを求め、フラグ設定
- INC Working Register、汎用レジスタ、Data Pointerの値を+1
- DEC Working Register、汎用レジスタ、Data Pointerの値を−1
プログラミングモデル
プログラミングモデルは、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)
目次
前
次