割り算

今度は、『動かしてわかるCPUの作り方10講』第9講までのCPUと、テキストとは別に追加したjnc命令とを使って割り算をしてみる。値はunsigned shortであると見なす。

0xABCD ÷ 0x0123 = 0b 0000 0000 1001 0111を計算した。
f:id:ti-nspire:20210206084439j:plain:w600
 
.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, _)