大学時代の2003年に初版本購入してから、電子工作部分で時間とられそうだからと、ずるずる本棚に積み上げていた書籍。
本書の評価は、Amazonレビューが全てを物語っている。
#amazon||>
||<
- CPUの創りかた・サポートページ
-- http://book.mycom.co.jp/support/e5/cpu/
- 「CPUの創りかたファンページ」
-- http://www.cable-net.ne.jp/user/takahisa/td4/fanpage.html
#more||
というわけで、実に7年間も「今読むべきか、先送りすべきか」とずるずる迷っていたのを、2日くらいで読みきってしまい、なんだか本書に対する姿勢がこれで良いのか軽い罪悪感を感じています。
・・・本当は電子回路の製作にも挑戦しようと思ってたんですよ。だからこそ、時間と根気が必要と思って、まとまった時間が取れるタイミングを狙ってずるずる引き延ばしてきちゃったんですが。
が、電子工作に片足突っ込んでた大学時代ならまだしも、ここ6年半田ごても握らず電子工作もしていない人間。
回路図だけじゃ、もうさすがに無理。諦めました。実体配線図+部品一式揃ったキットがあればやるかも。
実際の回路製作を抜きにしても、エミュレータが公開されてますので1bit単位で弄ってみて、機械語と戯れることも出来ます。
ソフトウェアオンリーの人でも楽しめるようにはなってますので、オススメ。
追記:後日、ROM部分をスポイル+モジュール化する事で製作ハードルを下げ、実際に製作してみました:
#ls|読書メモ/「CPUの創りかた」|
----
さすがに何も手を動かさないのは罪悪感が残ってスッキリしませんので、本書に出てくる機械語のエミュレータを自分で作ってみることにします。
今回はPythonで作ってみました。いろいろ手抜きをしていますが、特に大きな手抜きは「入力ポートのビットを途中で変更できない」点です。コマンドラインオプションで与えた値で固定されます。
$ python td4emu.py [-sN] [-iXXXX] file
-sN : 命令を実行するときのinterval-wait秒を指定。省略時は1秒。
-iXXXX : "1" or "0" で入力ポート4bitのON/OFFを指定。省略時は"0000"。
file : 機械語を保存したファイル名
機械語は、文字の"1", "0" で直接保存しちゃいます。一行一命令です。"1", "0", スペースのみ許可されます。スペースは実行時に削除されるので、適当に位置あわせなどに使います。
例(内容は適当です):
0010 0000
0000 0001
1110 0001
0110 0000
0101 0001
1001 0000
1110 0011
1111 0000
実行すると延々と動き続けます。Control+Cで終了します。
実行例(機械語の内容は適当です):
#pre||>
$ python td4emu.py -i1110 t01.txt
interval : 1 sec
input_port : 1110
source:
00100000
00000001
11100001
01100000
01010001
10010000
11100011
11110000
-----
hit ENTER to start:
[00] 0010 0000 => [A:1110],[B:0000],[C:0],[IP:0000],[OUT:0000]
[01] 0000 0001 => [A:1111],[B:0000],[C:0],[IP:0001],[OUT:0000]
[02] 1110 0001 => [A:1111],[B:0000],[C:0],[IP:0001],[OUT:0000]
[01] 0000 0001 => [A:0000],[B:0000],[C:1],[IP:0001],[OUT:0000]
[02] 1110 0001 => [A:0000],[B:0000],[C:0],[IP:0010],[OUT:0000]
Traceback (most recent call last):
File "td4emu.py", line 159, in
time.sleep(interval_sleep)
KeyboardInterrupt
||<
以下、ソースです。10進数→2進数文字列の変換にbin()関数を使ってますので、Python2.6以降が必要のはずです。
td4emu.py:
#code|python|>
import sys
import string
import re
import time
if len(sys.argv) < 2:
print "usage %s [-sN] [-i0000] file" % sys.argv[0]
sys.exit(1)
interval_sleep = 1
input_port = '0000'
for arg in sys.argv[1:]:
if 0 == arg.find('-i'):
input_port = arg.replace('-i', '')
if not re.match(r'[01]{4}', input_port):
print "-iXXXX : only 0 or 1, 4char"
sys.exit(1)
continue
if 0 == arg.find('-s'):
interval_sleep = int(arg.replace('-s', ''))
continue
filename = arg
f = open(filename, 'r')
def op_strip(v):
v = v.replace(' ', '')
return string.strip(v)
opcodes = map(op_strip, f.readlines())
print "interval : %d sec" % interval_sleep
print "input_port : %s" % input_port
print "source:"
for opcode in opcodes:
print opcode
print "-----"
raw_input("hit ENTER to start:")
reg = {
'a':'0000',
'b':'0000',
'ip':'0000',
'c':False,
'out':'0000'
}
def int2bin4(v):
v = 15 & v
t = bin(v)
t = t.replace('0b', '')
f = 4 - len(t)
f = '0' * f
return f + t
def op_mov_a(reg, im):
reg['a'] = im
reg['c'] = False
def op_mov_b(reg, im):
reg['b'] = im
reg['c'] = False
def op_mov_a_b(reg, im):
reg['a'] = reg['b']
reg['c'] = False
def op_mov_b_a(reg, im):
reg['b'] = reg['a']
reg['c'] = False
def op_add_a(reg, im):
t1 = int(reg['a'], 2)
t2 = int(im, 2)
t3 = t1 + t2
reg['c'] = False
if 15 < t3:
reg['c'] = True
reg['a'] = int2bin4(t3)
def op_add_b(reg, im):
t1 = int(reg['b'], 2)
t2 = int(im, 2)
t3 = t1 + t2
reg['c'] = False
if 15 < t3:
reg['c'] = True
reg['b'] = int2bin4(t3)
def op_in_a(reg, im):
reg['a'] = input_port
reg['c'] = False
def op_in_b(reg, im):
reg['b'] = input_port
reg['c'] = False
def op_out(reg, im):
reg['out'] = im
reg['c'] = False
def op_out_b(reg, im):
reg['out'] = reg['b']
reg['c'] = False
def op_jmp(reg, im):
reg['ip'] = im
reg['c'] = False
def op_jnc(reg, im):
v = reg['c']
reg['c'] = False
if not v:
reg['ip'] = im
op_dict = {
'0011' : op_mov_a,
'0111' : op_mov_b,
'0001' : op_mov_a_b,
'0100' : op_mov_b_a,
'0000' : op_add_a,
'0101' : op_add_b,
'0010' : op_in_a,
'0110' : op_in_b,
'1011' : op_out,
'1001' : op_out_b,
'1111' : op_jmp,
'1110' : op_jnc,
}
real_ip = 0
while True:
if real_ip > len(opcodes) - 1:
real_ip = 0
reg['ip'] = int2bin4(real_ip)
ip_old = reg['ip']
cur_op = opcodes[real_ip]
op = cur_op[0:4]
im = cur_op[4:]
if not (op in op_dict):
print "unknown op : %s, addr : %s" % (op, real_ip)
sys.exit(1)
print "[%02d] %s %s => " % (real_ip, op, im),
op_dict[op](reg, im)
if reg['ip'] != ip_old:
real_ip = int(reg['ip'], 2)
else:
real_ip += 1
print "[A:%s],[B:%s],[C:%s],[IP:%s],[OUT:%s]" % (
reg['a'], reg['b'], ('1' if reg['c'] else '0'), reg['ip'], reg['out'])
time.sleep(interval_sleep)
||<
以上。