今度は、『動かしてわかるCPUの作り方10講』第9講までのCPUと、テキストとは別に追加したjnc
命令とを使って割り算をしてみる。値はunsigned short
であると見なす。
0xABCD ÷ 0x0123 = 0b 0000 0000 1001 0111を計算した。
.mifファイルを生成するアセンブラのようなもの:
def generate_mif(file_name, width, depth, rom): f = open(file_name, "w") f.write("WIDTH=%d;\n" % width) f.write("DEPTH=%d;\n" % depth) f.write("ADDRESS_RADIX=UNS;\n") f.write("DATA_RADIX=BIN;\n") f.write("CONTENT BEGIN\n") format_of_code = "0" + str(width) + "b" for i in range(depth): machine_code = format(rom[i], format_of_code) f.write("%10d : %s;\n" % (i, machine_code)) f.write("END;\n") f.close() r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ def mov(ra, rb): return 0 << 11 | ra << 8 | rb << 5 def add(ra, rb): return 1 << 11 | ra << 8 | rb << 5 def sub(ra, rb): return 2 << 11 | ra << 8 | rb << 5 def _and(ra, rb): return 3 << 11 | ra << 8 | rb << 5 def _or(ra, rb): return 4 << 11 | ra << 8 | rb << 5 def sl(ra): return 5 << 11 | ra << 8 # 左シフト def sr(ra): return 6 << 11 | ra << 8 # 右シフト def sra(ra): return 7 << 11 | ra << 8 # 算術右シフト def ldl(ra, ival): return 8 << 11 | ra << 8 | (ival & 0xFF) # 下位バイトに即値をロード def ldh(ra, ival): return 9 << 11 | ra << 8 | (ival & 0xFF) # 上位バイトに即値をロード def cmp(ra, rb): return 10 << 11 | ra << 8 | rb << 5 # 一致したらフラグが立つ。 def je(addr): return 11 << 11 | (addr & 0xFF) # 一致フラグが立っていたらジャンプ def jmp(addr): return 12 << 11 | (addr & 0xFF) # 無条件ジャンプ def ld(ra, addr): return 13 << 11 | ra << 8 | (addr & 0xFF) # RAMからレジスタへ値を持ってくる。 def st(ra, addr): return 14 << 11 | ra << 8 | (addr & 0xFF) # レジスタの値をRAMへ格納する。 #def hlt(): return 15 << 11 def jnc(addr): return 15 << 11 | (addr & 0xFF) # jump if no carry def nop() : return 0 def _in(ra): return ld(ra, 65) # INポートからレジスタへ読み込む。 def out(ra): return st(ra, 64) # レジスタからOUTポートへ書き出す。 # レジスタへ2バイト即値をロード(★2命令★) def ld_hl(ra, two_bytes): return ldh(ra, two_bytes >> 8), ldl(ra, two_bytes & 0xFF) ########################################## # RAM[0..7]はレジスタr0..7の退避用にする # ########################################## # インクリメント命令(★5命令★) def incre(ra): # 何かに使っているかもしれないレジスタをRAMに退避して、増分をセットして、インクリメントして、退避しておいたレジスタを元に戻す。 return st(r7, 7), *ld_hl(r7, 1), add(ra, r7), ld(r7, 7) # デクリメント命令(★5命令★) def decre(ra): # 何かに使っているかもしれないレジスタをRAMに退避して、減分をセットして、デクリメントして、退避しておいたレジスタを元に戻す。 return st(r7, 7), *ld_hl(r7, 1), sub(ra, r7), ld(r7, 7) FILE_NAME = "rom_init.mif" WIDTH = 15 DEPTH = 256 _ = [0] * DEPTH # N ÷ D = Qを計算する。 INC = r0 N = r1 D = r2 Q = r3 _[0] = ldl(INC, 1) # 増分をセットして、 _[1], _[2] = ld_hl(N, 0xABCD) # 割られる数をセットして、 _[3] = _in(D) # 割る数をINポートから読み込んで、 _[4] = add(Q, INC) # 引いた回数を先にインクリメントしてから、 _[5] = out(Q) # 確認のため、ここまでに引いた回数を出力して、 _[6] = sub(N, D) # 実際に引いて、 _[7] = jnc(4) # キャリー(というかボロー)が発生していなければ、また引き算を繰り返すが、 _[8] = sub(Q, INC) # キャリー(というかボロー)が発生していたらもう引けないので、1回多く引いたぶんだけ商を戻して、 _[9] = add(N, D) # 余りも1回分だけ戻して、 _[10] = out(Q) # 商を出力して、 _[11] = st(N, 63) # 余りをとっておいて、 _[12] = jmp(12) # ここにとどまる。 """ _[0], _[1] = ld_hl(N, 0xABCD) # 割られる数をセットして、 _[2] = _in(D) # 割る数をINポートから読み込んで、 _[3], _[4], _[5], _[6], _[7] = incre(Q) # 引いた回数を先にインクリメントしてから、 _[8] = out(Q) # 確認のため、ここまでに引いた回数を出力して、 _[9] = sub(N, D) # 実際に引いて、 _[10] = jnc(3) # キャリー(というかボロー)が発生していなければ、また引き算を繰り返すが、 _[11], _[12], _[13], _[14], _[15] = decre(Q) # キャリー(というかボロー)が発生していたらもう引けないので、1回多く引いたぶんだけ商を戻して、 _[16] = add(N, D) # 余りも1回分だけ戻して、 _[17] = out(Q) # 商を出力して、 _[18] = st(N, 63) # 余りをとっておいて、 _[19] = jmp(19) # ここにとどまる。 """ generate_mif(FILE_NAME, WIDTH, DEPTH, _)