大学時代の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) ||< 以上。