目次
前
次
GMC-4アセンブラ(Python)
学研の「大人の科学」シリーズの中に、4ビットの
マイクロコンピュータがあります。
アセンブリ言語のニモニックをハンドアセンブルして
16進数に変換してから、入力するのは面倒と思いました。
面倒なことは、コンピュータにやらせればよいと
考えて、スクリプト言語でアセンブラを作ること
にしました。
アセンブリ言語のニモニックとコードの対応は
以下のようになっています。
- KA 0
- AO 1
- CH 2
- CY 3
- AM 4
- MA 5
- M+ 6
- M- 7
- TIA 8
- AIA 9
- TIY A
- AIY B
- CIA C
- CIY D
- JUMP F
- CAL_RSTO E0
- CAL_SETR E1
- CAL_RSTR E2
- CAL_CMPL E4
- CAL_CHNG E5
- CAL_SIFT E6
- CAL_ENDS E7
- CAL_ERRS E8
- CAL_SHTS E9
- CAL_LONS EA
- CAL_SUND EB
- CAL_TIMR EC
- CAL_DSPR ED
- CAL_DEM+ EE
- CAL_DEM- EF
命令によっては、オペランド(パラメータ)を
必要とするものがありますが、基本は1ニブル
(ニブルは4ビット)。
アセンブラは、ニモニックを数値に置換する
プログラムと考えれば、簡単な変換の処理を
スクリプトで記述すればよいとわかります。
簡単なプログラムを記述し、GMC-4のキー入力が
やりやすいように、CUIでの操作イメージを作って
みます。
利用したプログラムテキストは、以下。
KA
AO
TIY 1
TIA 2
AIA 3
JUMP 2
GMC-4の仕様を調べると、オペランド(パラメータ)を
必要とする命令は、次の7種でした。
- TIA
- AIA
- TIY
- AIY
- CIA
- CIY
- JUMP
これらの命令は、2あるいは3ニブルのコードに
変換します。3ニブルに変換するのはJUMPのみで
この場合、独自変換処理を作ることにします。
基本命令のほかに、システムが用意している
サブルーチンを呼出し、使うこともあります。
サブルーチンは、すべて2ニブルになるので
アセンブラは、次の4処理に分割して、記述
すればよいとわかります。
- 基本命令で1ニブル
- 基本命令で2ニブル
- JUMPで3ニブル
- サブルーチンで2ニブル
ここまでわかったなら、必要なスクリプトを記述
していきます。
命令テーブル作成
ニモニックの文字列と命令コードの数値は、1:1に
対応します。変換に利用するテーブル(表)を辞書を
利用して作成します。
# generate disctionary
mdic = {}
mdic["KA"] = 0
mdic["AO"] = 1
mdic["CH"] = 2
mdic["CY"] = 3
mdic["AM"] = 4
mdic["MA"] = 5
mdic["M+"] = 6
mdic["M-"] = 7
mdic["TIA"] = 8
mdic["AIA"] = 9
mdic["TIY"] = 10
mdic["AIY"] = 11
mdic["CIA"] = 12
mdic["CIY"] = 13
mdic["JUMP"] = 15
mdic["CAL_RSTO"] = 224
mdic["CAL_SETR"] = 225
mdic["CAL_RSTR"] = 226
mdic["CAL_CMPL"] = 228
mdic["CAL_CHNG"] = 229
mdic["CAL_SIFT"] = 230
mdic["CAL_ENDS"] = 231
mdic["CAL_ERRS"] = 232
mdic["CAL_SHTS"] = 233
mdic["CAL_LONS"] = 234
mdic["CAL_SUND"] = 235
mdic["CAL_TIMR"] = 236
mdic["CAL_DSPR"] = 237
mdic["CAL_DEM+"] = 238
mdic["CAL_DEM-"] = 239
print mdic
辞書ができたかをチェックします。
プログラム入力
アセンブラは、ニモニックで指定した文字列を数値
に変換するのが仕事。プログラムは、ファイル内に
記述するので、1行ごとに分割し、リストに入れて
から、1行ごとに変換します。
ファイルからプログラムを入力し、1行ごとにリスト
の要素にする処理をテストします。
# open
fin = open('tst.asm','r')
# get
line = fin.read()
# close
fin.close()
# generate list
glist = line.split('\n')
# delete space
alist = []
for e in glist :
if e != '' :
alist.append( e )
print alist
リストの要素が、1行を構成しているのかを
見てみます。
リストの各要素は、プログラムの1行に分割されて
いると確認できました。
ファイル名を、常に同じにしておき、既存のプログラムは
別ファイル名にして保存するとします。
アセンブル処理
アセンブルは、4種類に分割して実現します。
サブルーチンと基本命令に分けて、処理を記述して
みます。
# delete space
def cmdCode(x):
# loop
result = []
for e in x :
if e != '' :
result.append( e )
return result
#
def havOperand(x) :
result = False
if x == "TIA" or x == "AIA" :
result = True
if x == "TIY" or x == "AIY" :
result = True
if x == "CIY" or x == "CIA" :
result = True
if x == "JUMP" :
result = True
return result
#
def convHex(x) :
hx = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')
return hx[x]
#
def convHex2(x) :
# clear
result = ""
# separate
dh = x / 16
dl = x % 16
# concatenate
result += convHex(dh)
result += convHex(dl)
return result
#
def isSubCode(x) :
result = False
if len(x) > 4 :
result = True
return result
# generate disctionary
mdic = {}
mdic["KA"] = 0
mdic["AO"] = 1
mdic["CH"] = 2
mdic["CY"] = 3
mdic["AM"] = 4
mdic["MA"] = 5
mdic["M+"] = 6
mdic["M-"] = 7
mdic["TIA"] = 8
mdic["AIA"] = 9
mdic["TIY"] = 10
mdic["AIY"] = 11
mdic["CIA"] = 12
mdic["CIY"] = 13
mdic["JUMP"] = 15
mdic["CAL_RSTO"] = 224
mdic["CAL_SETR"] = 225
mdic["CAL_RSTR"] = 226
mdic["CAL_CMPL"] = 228
mdic["CAL_CHNG"] = 229
mdic["CAL_SIFT"] = 230
mdic["CAL_ENDS"] = 231
mdic["CAL_ERRS"] = 232
mdic["CAL_SHTS"] = 233
mdic["CAL_LONS"] = 234
mdic["CAL_SUND"] = 235
mdic["CAL_TIMR"] = 236
mdic["CAL_DSPR"] = 237
mdic["CAL_DEM+"] = 238
mdic["CAL_DEM-"] = 239
# open
fin = open('tst.asm','r')
# get
line = fin.read()
# close
fin.close()
# generate list
glist = line.split('\n')
# delete space
alist = []
for e in glist :
if e != '' :
alist.append( e )
print alist
# convert
pc = 0
for e in alist :
# get mnemonic
tmp = cmdCode( e.split(' ') )
# get instruction
cmd = tmp[0]
# judge
if isSubCode(cmd) == True :
# separate
dh = int(mdic[cmd]) / 16
dl = int(mdic[cmd]) % 16
# first nibble
print convHex2(pc),convHex( dh )
pc = pc+1
# second nibble
print convHex2(pc),convHex( dl )
pc = pc+1
定義して利用した関数もありますが、実行すると
次のようになります。
サブルーチンが、2ニブルに変換されています。
新たに定義した関数の内容を説明します。
スペース削除
1行の中には、命令とオペランドが記述されています。
命令、オペランドに分割してから、数値に変換しますが
スペースはない方が処理しやすいので、スペース削除の
関数を定義しました。
def cmdCode(x):
# loop
result = []
for e in x :
if e != '' :
result.append( e )
return result
サブルーチン命令の判定
サブルーチン命令は、ニモニックの文字列だけで
判定できます。該当する文字列ならば、論理値を
返すようにしました。
def isSubCode(x) :
result = False
if len(x) > 4 :
result = True
return result
数値から16進数数字列変換
命令は、16進の1文字か2文字になるので
タプルを利用して変換します。
# hexadecimal -> digit
def convHex(x) :
hx = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')
return hx[x]
# hexadecimal -> digit
def convHex2(x) :
# clear
result = ""
# separate
dh = x / 16
dl = x % 16
# concatenate
result += convHex(dh)
result += convHex(dl)
return result
オペランドをもつ命令の判定
1ニブルで表現できる命令と2ニブル以上になる命令は
異なります。2ニブル以上となる命令か否かを論理値で
返すようにしました。
def havOperand(x) :
result = False
if x == "TIA" or x == "AIA" :
result = True
if x == "TIY" or x == "AIY" :
result = True
if x == "CIY" or x == "CIA" :
result = True
if x == "JUMP" :
result = True
return result
基本命令の処理を付加して、アセンブラを完成させました。
# delete space
def cmdCode(x):
# loop
result = []
for e in x :
if e != '' :
result.append( e )
return result
#
def havOperand(x) :
result = False
if x == "TIA" or x == "AIA" :
result = True
if x == "TIY" or x == "AIY" :
result = True
if x == "CIY" or x == "CIA" :
result = True
if x == "JUMP" :
result = True
return result
# hexadecimal -> digit
def convHex(x) :
hx = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')
return hx[x]
# hexadecimal -> digit
def convHex2(x) :
# clear
result = ""
# separate
dh = x / 16
dl = x % 16
# concatenate
result += convHex(dh)
result += convHex(dl)
return result
# check subroutine
def isSubCode(x) :
result = False
if len(x) > 4 :
result = True
return result
# generate disctionary
mdic = {}
mdic["KA"] = 0
mdic["AO"] = 1
mdic["CH"] = 2
mdic["CY"] = 3
mdic["AM"] = 4
mdic["MA"] = 5
mdic["M+"] = 6
mdic["M-"] = 7
mdic["TIA"] = 8
mdic["AIA"] = 9
mdic["TIY"] = 10
mdic["AIY"] = 11
mdic["CIA"] = 12
mdic["CIY"] = 13
mdic["JUMP"] = 15
mdic["CAL_RSTO"] = 224
mdic["CAL_SETR"] = 225
mdic["CAL_RSTR"] = 226
mdic["CAL_CMPL"] = 228
mdic["CAL_CHNG"] = 229
mdic["CAL_SIFT"] = 230
mdic["CAL_ENDS"] = 231
mdic["CAL_ERRS"] = 232
mdic["CAL_SHTS"] = 233
mdic["CAL_LONS"] = 234
mdic["CAL_SUND"] = 235
mdic["CAL_TIMR"] = 236
mdic["CAL_DSPR"] = 237
mdic["CAL_DEM+"] = 238
mdic["CAL_DEM-"] = 239
# open
fin = open('tst.asm','r')
# get
line = fin.read()
# close
fin.close()
# generate list
glist = line.split('\n')
# delete space
alist = []
for e in glist :
if e != '' :
alist.append( e )
# convert
pc = 0
for e in alist :
# get mnemonic
tmp = cmdCode( e.split(' ') )
# get instruction
cmd = tmp[0]
# judge
if isSubCode(cmd) == True :
# separate
dh = int(mdic[cmd]) / 16
dl = int(mdic[cmd]) % 16
# first nibble
print convHex2(pc),convHex( dh )
pc = pc+1
# second nibble
print convHex2(pc),convHex( dl )
pc = pc+1
else :
# first nibble
print convHex2(pc),convHex( int(mdic[cmd]) )
pc = pc + 1
# second nibble and third nibble
if havOperand(cmd) == True :
if cmd == "JUMP" :
# separate upper and lower
dh = int(tmp[1]) / 16
dl = int(tmp[1]) % 16
# second nibble
print convHex2(pc),convHex(dh)
pc = pc + 1
# third nibble
print convHex2(pc),convHex(dl)
pc = pc + 1
else :
print convHex2(pc),convHex(int(tmp[1]))
pc = pc + 1
アセンブル結果は、I/Oリダイレクトを利用して
テキストファイルに保存します。
テキストファイルにした内容は、次のようになります。
00 0
01 1
02 A
03 1
04 8
05 2
06 9
07 3
08 E
09 8
0A E
0B 0
0C E
0D 1
0E E
0F 2
10 E
11 4
12 E
13 5
14 E
15 6
16 E
17 7
18 E
19 8
1A E
1B 9
1C E
1D A
1E E
1F B
20 E
21 C
22 E
23 D
24 E
25 E
26 E
27 F
28 F
29 1
2A 1
アドレスは、2進数のLED表示になるので
2桁の16進アドレスを0と1の組み合わせ
にしておきます。変換は、次のスクリプト
でよいでしょう。
#
def convBinary(x):
hx = ('0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110','1111')
# separate
dh = x / 16
dl = x % 16
result = ""
result += hx[dh]
result += hx[dl]
return result[2:]
# open
fin = open('result.txt','r')
# get
line = fin.read()
# close
fin.close()
# generate list
glist = line.split('\n')
# delete space
alist = []
for e in glist :
if e != '' :
alist.append( e )
for e in alist :
tmp = e.split(' ')
print convBinary(int(tmp[0],16)),tmp[1]
このスクリプトで変換すると、以下となります。
000000 0
000001 1
000010 A
000011 1
000100 8
000101 2
000110 9
000111 3
001000 E
001001 8
001010 E
001011 0
001100 E
001101 1
001110 E
001111 2
010000 E
010001 4
010010 E
010011 5
010100 E
010101 6
010110 E
010111 7
011000 E
011001 8
011010 E
011011 9
011100 E
011101 A
011110 E
011111 B
100000 E
100001 C
100010 E
100011 D
100100 E
100101 E
100110 E
100111 F
101000 F
101001 1
101010 1
2進数の6桁は、スライスを利用しました。
目次
前
次