2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

『動かしてわかるCPUの作り方10講』(技術評論社)第9講までをFPGA MAX10 + 回路図エディターで作る

Last updated at Posted at 2021-03-07

1. 概要

動かしてわかるCPUの作り方10講』(井澤裕司、技術評論社)第9講までのCPUをFPGA MAX10 + 回路図エディターで作る。

MAX10 (10M08SAE144C8G)の評価ボードはマルツ製を使った(参考: 『MAX10実験キットで学ぶFPGA&コンピュータ』(圓山宗智、CQ出版)。

単にテキストのVHDLをシンボルで書き換えただけであるが、命令は一部以下のように差し替えた。

  • hlt (停止)命令を廃止してjnc(jump if no carry)命令を追加した。
  • sra (算術右シフト)命令を廃止してjmp_reg (16ビットレジスタの下位バイトに記録してあるアドレスへジャンプ)命令を追加した。

2. コンポーネントごとに作ってゆく

ここで作るCPUは以下のコンポーネントで構成される。

  • 4相クロック生成部(clk_gen)
  • プログラムメモリーからの読み出し部(fetch_rom)
  • レジスタからの読み出し部(reg_dc)
  • レジスタへの書き込み部(reg_wb)
  • RAMの読み出し/書き込み部(mega_ram)
  • 演算部(exec)

2.1 4相クロック生成部(clk_gen)を作る

いわゆるone-hot state counterである。
image.png
image.png

2.2 プログラムメモリーからの読み出し部(fetch_rom)を作る

Quartus PrimeのIP Catalogに登録されているROM: 1-PORTを使って15ビット × 256本のROMを作る。作りかたはhttps://ti-nspire.hatenablog.com/entry/2021/01/28/063115を参照。ROMの初期化ファイルである.mifファイルは、あとの「4. サンプルプログラム」のなかで紹介するPythonスクリプトで生成する

image.png

生成された.mifファイルの例:

WIDTH=15;
DEPTH=256;
ADDRESS_RADIX=UNS;
DATA_RADIX=BIN;
CONTENT BEGIN
         0   :   100100000000000;
         1   :   100000000000001;
         2   :   100100100000001;
         ()
       254   :   111010001000000;
       255   :   110000011111111;
END;

2.3 レジスタからの読み出し部(reg_dc)を作る

8本の16ビットレジスタのうち1本を選んで読み出す。
image.png
image.png

2.4 レジスタへの書き込み部(reg_wb)を作る

8本の16ビットレジスタのうち1本を選んで書き込む。

2.4.1 まず16ビットレジスタを1個作る

image.png
image.png

2.4.2 それを8個連結する

image.png
image.png

2.5 RAMの読み出し/書き込み部(mega_ram)を作る

IP CatalogにあるRAM: 1-PORTを使って16ビット × 64本のRAMを作る。RAM: 1-PORTの使い方はROM: 1-PORTと同じ。
image.png
image.png

2.6 演算部(exec)を作る

2.6.1 プログラムカウンターを作る

sclr、sloadのsはsynchronous。
image.png
image.png

2.6.2 加減算器を作る

追加したjnc命令用にキャリーを得たいので加減算自体は17ビットで行う。

2.6.2.1 2の補数回路を作る

image.png
image.png

2.6.2.2 組み合わせて加減算器にする

image.png
image.png

2.6.3 全部組み合わせてexec部を作る

一部命令を差し替えたため、加減算器からキャリーを取り出している点(jnc命令用)と、プログラムカウンターのロード値が選べるようになっている点(jmp_reg命令用)とがテキストと違っている。
image.png
image.png

2.7 すべてのコンポーネントを組み合わせてCPUを作る

image.png
image.png

2.8 ソースクロック生成部を作る

IP Catalogに登録されているALTPLLを利用する。ここでは、評価ボードに載っている48 MHz発振器から1 MHzを合成した。1 MHzにした理由は特にない。ALTPLLの使いかたはhttps://ti-nspire.hatenablog.com/entry/2021/01/25/071741を参照。
image.png

3. 端子を割り当てる

下のように割り当てた。評価ボード上の48 MHz発振器がMAX10 (10M08SAE144C8G)の27番ピンに接続してある。コンパイルする前に.mifファイルをQuartus Primeのプロジェクトファイルと同じディレクトリに入れておく。
image.png
image.png
(↑この図は、『MAX10実験キットで学ぶFPGA&コンピュータ』附録ディスクに収録されているMAX10-FB_Document_Rev03_20150911.pdfから)
image.png

4. サンプルプログラム

4.1 RAMのテストをする

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_ram_test.py
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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


ad = -1

# ram 0番地に値100をストアする。
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(r0, 100)
_[(ad := ad+1)] = st(r0, 0)

# ram 11番地に値200をストアする。
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(r0, 200)
_[(ad := ad+1)] = st(r0, 11)

# ram 63番地に値300をストアする。
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(r0, 300)
_[(ad := ad+1)] = st(r0, 63)

_[(ad := ad+1)] = ld(r0, 0)   # ram 0番地の値をレジスタ0に持ってきて、
_[(ad := ad+1)] = ld(r1, 11)  # ram 11番地の値をレジスタ1に持ってきて、
_[(ad := ad+1)] = add(r0, r1) # 両者を足し合わせて、

_[(ad := ad+1)] = ld(r1, 63)  # ram 63番地の値をレジスタ1に持ってきて、
_[(ad := ad+1)] = add(r0, r1) # さらに両者を足し合わせて、

_[(ad := ad+1)] = out(r0)     # 足した結果(0d600 = 0b 0000 0010 0101 1000)をOUTポートから出力して、
_[(ad := ad+1)] = jmp(ad+1)   # ここにとどまる。


generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2532.JPG

4.2 1から361までの総和を求める

sigma(n, n=1 to 361) = 65341 (0b 1111 1111 0011 1101)

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_sum_of_1to361.py
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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


total        = r0
inc          = r1
operand      = r2
num_of_loops = r3

ad = -1

# 増分をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(inc, 1)

# ループ回数をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(num_of_loops, 361)

jump_where = ad+1
_[(ad := ad+1)] = add(operand, r1)           # 足す数をインクリメントして、
_[(ad := ad+1)] = add(total, operand)        # それをここまでの総和に加算して、
_[(ad := ad+1)] = cmp(operand, num_of_loops) # 足す数 = ループ回数であるなら、
_[(ad := ad+1)] = je(ad+3)                   # ループを抜けて、
_[(ad := ad+1)] = jmp(jump_where)            # そうでなければ加算を繰り返して、

_[(ad := ad+1)] = out(total) # 総和(0d65341 = 0b 1111 1111 0011 1101)をOUTポートから出力して、
_[(ad := ad+1)] = jmp(ad+1)  # ここにとどまる。



generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2535.JPG

4.3 掛け算

300 * 200 = 60000 (0b 1110 1010 0110 0000)

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_multiply.py
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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


# M × N = Pを計算する。
INC   = r0
M     = r1
N     = r2
COUNT = r3
P     = r4


ad = -1

# 増分をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(INC, 1)
      
# 掛けられる数Mをセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(M, 300)

# 掛ける数Nをセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(N, 200)

jump_where = ad+1
_[(ad := ad+1)] = cmp(COUNT, N)   # N回足し終えたら、
_[(ad := ad+1)] = je(254)         # ループを抜けるが、そうでなければ、
_[(ad := ad+1)] = add(P, M)       # 加算して、 
_[(ad := ad+1)] = add(COUNT, INC) # 加算回数をインクリメントして、
_[(ad := ad+1)] = jmp(jump_where) # ループを繰り返して、 

_[254] = out(P)   # 積(300 * 200 = 60000 (0b 1110 1010 0110 0000))をOUTポートから出力して、
_[255] = jmp(255) # ここにとどまる。


generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2539.JPG

4.4 割り算

65000 ÷ 51 = 1274 (0b 0000 0100 1111 1010)

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_divide.py
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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


# N ÷ D = Qを計算する。
INC = r0
N = r1
D = r2
Q = r3

ad = -1

# 増分をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(INC, 1)

# 割られる数をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(N, 65000)

# 割る数をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(D, 51)

jump_where = ad+1
_[(ad := ad+1)] = add(Q, INC)     # 引く前に、引いた回数をインクリメントしてから、
_[(ad := ad+1)] = sub(N, D)       # 実際に引いて、
_[(ad := ad+1)] = jnc(jump_where) # キャリー(というかボロー)が発生していなければ、また引き算を繰り返すが、

_[(ad := ad+1)] = sub(Q, INC) # キャリー(というかボロー)が発生していたらもう引けないので、1回多く引いたぶんだけ商を戻して、
_[(ad := ad+1)] = add(N, D)   # 余りも1回ぶんだけ戻して、

_[(ad := ad+1)] = out(Q)    # 商(65000 / 51 = 1274 (0b 0000 0100 1111 1010))をOUTポートから出力して、
#_[(ad := ad+1)] = st(N, 63) # 余りをとっておいて、
_[(ad := ad+1)] = jmp(ad+1) # ここにとどまる。



generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2542.JPG

4.5 Lチカ

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_blink.py
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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH



F_CPU = 1E6
DELAY = 0.2 # sec
LOOPS = int(DELAY * F_CPU / 8)

LED     = r0
DEC     = r1
COUNTER = r2

ad = -1

# 減分をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(DEC, 1)

jump_where_0 = ad+1
# ledを点けて、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(LED, 0xAAAA)
_[(ad := ad+1)] = out(LED)

# ループ回数 - 1をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(COUNTER, LOOPS - 1)

jump_where_1 = ad+1
# 少し待って、
_[(ad := ad+1)] = sub(COUNTER, DEC) # ループ回数をデクリメントして、
_[(ad := ad+1)] = jnc(jump_where_1) # ループ回数が0を下回るまでループを繰り返して、

# 点灯パターンを変えて、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(LED, 0xAAAA >> 1)
_[(ad := ad+1)] = out(LED)

# ループ回数 - 1をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(COUNTER, LOOPS - 1)

jump_where_2 = ad+1
# 少し待って、
_[(ad := ad+1)] = sub(COUNTER, DEC) # ループ回数をデクリメントして、
_[(ad := ad+1)] = jnc(jump_where_2) # ループ回数が0を下回るまでループを繰り返して、


_[(ad := ad+1)] = jmp(jump_where_0) # をずっと繰り返す。



generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果(↓YouTubeへリンク):
image.png

4.6 Lチカ(ビジーループをサブルーチンっぽくする)

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_blink_with_subroutine.py
import math

def constrain(val, _min, _max):
    return int(min(max(val, _min), _max))

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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


TMP0    = r0
TMP1    = r1
RET_TO  = r7


# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret(): return jmp_reg(RET_TO)



FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH

F_CPU  = 1E6
PERIOD = 1 / F_CPU
DELAY  = 0.2 # sec
LOOPS  = (DELAY - 20 * PERIOD)/(8 * PERIOD) # 5命令×4クロック + 2命令×4クロック×ループ数 = 遅延時間
LOOPS  = constrain(LOOPS, 1, 2**16-1)

###############
# subroutines #
###############

# BUSY_LOOP:
BUSY_LOOP = 245 # ~251
_[(ad := BUSY_LOOP)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS - 1)
jump_sub_where = ad+1
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(jump_sub_where)
_[(ad := ad+1)] = ret()


################
# main routine #
################

ad = -1
jump_where = ad+1

# LEDを点けて、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 0xFF00)
_[(ad := ad+1)] = out(TMP0)

# 少し待って、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

# 点灯パターンを変えて、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 0x00FF)
_[(ad := ad+1)] = out(TMP0)

# 少し待って、最初に戻って、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, jump_where)



generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果(↓YouTubeへリンク):
image.png

4.7 ナイトライダー

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_knight_rider.py
import math

def constrain(val, _min, _max):
    return int(min(max(val, _min), _max))

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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ


TMP0    = r0
TMP1    = r1

SHIFT   = r2

LED     = r4
RET_TO  = r7


# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret(): return jmp_reg(RET_TO)



FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH

F_CPU  = 1E6
PERIOD = 1 / F_CPU
DELAY  = 0.02 # sec
LOOPS  = (DELAY - 20 * PERIOD)/(8 * PERIOD) # 5命令×4クロック + 2命令×4クロック×ループ数 = 遅延時間
LOOPS  = constrain(LOOPS, 1, 2**16-1)

###############
# subroutines #
###############

# BUSY_LOOP:
BUSY_LOOP = 245 # ~251
_[(ad := BUSY_LOOP)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS - 1)
jump_sub_where = ad+1
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(jump_sub_where)
_[(ad := ad+1)] = ret()


################
# main routine #
################

ad = -1
jump_where_0 = ad+1


_[(ad := ad+1)] = ldl(LED, 0b1)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)] = ldl(LED, 0b11)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)] = ldl(LED, 0b111)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)] = ldl(LED, 0b1111)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)



_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(SHIFT, 14)
jump_where_1 = ad+1

_[(ad := ad+1)] = sl(LED)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)      
_[(ad := ad+1)] = sub(SHIFT, TMP0)
_[(ad := ad+1)] = jnc(jump_where_1)



_[(ad := ad+1)] = ldh(LED, 0b11000000)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)] = ldh(LED, 0b11100000)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)] = ldh(LED, 0b11110000)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)



_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(SHIFT, 13)
jump_where_2 = ad+1

_[(ad := ad+1)] = sr(LED)
_[(ad := ad+1)] = out(LED)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(BUSY_LOOP, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)      
_[(ad := ad+1)] = sub(SHIFT, TMP0)
_[(ad := ad+1)] = jnc(jump_where_2)



_[(ad := ad+1)] = jmp(jump_where_0)


generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果(↓YouTubeへリンク):
image.png

4.8 キャラクターLCDでhello, world

3.3 Vで直接動かせるSC1602BBWB-XA-LB-Gを使った。

CPUとLCDとの接続は下のとおりとした。
image.png

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_lcd_hello_world.py
import math

def constrain(val, _min, _max):
    return int(min(max(val, _min), _max))

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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ

TMP0    = r0
TMP1    = r1
OUT     = r2
RET_TO  = r7

# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret(): return jmp_reg(RET_TO)



FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


F_CPU  = 1E6
PERIOD = 1 / F_CPU


DELAY_40ms = 40E-3
DELAY_2ms  = 2E-3
DELAY_50us = 50E-6

LOOPS_for_40ms = (DELAY_40ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_40ms = constrain(LOOPS_for_40ms, 1, 2**16-1)

LOOPS_for_2ms = (DELAY_2ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_2ms = constrain(LOOPS_for_2ms, 1, 2**16-1)

LOOPS_for_50us = (DELAY_50us - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_50us = constrain(LOOPS_for_50us, 1, 2**16-1)


FUNC_SET = 0b111000 # 1, 8/4 bits, 2/1 lines, 10/8 dots, don't care, don't care
DISP_CNT = 0b1100   # 1, 文字表示, カーソル表示, カーソル点滅
CLS      = 0b1      # 表示を消去
ENT_MOD  = 0b110    # 1, 右/左シフト, 表示範囲全体シフト有効化


###############
# subroutines #
###############
ad = 199

# DELAY_LONG:
DELAY_LONG = ad+1
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_40ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# COMMAND_W:
COMMAND_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b10)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_50us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0) 
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DELAY_SHORT:
DELAY_SHORT = ad+1
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_2ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DATA_W:
DATA_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b11)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b01)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_50us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()


################
# main routine #
################

ad = -1

# 最初にLCDをdisableにする。
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)

# CALL DELAY_40ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_LONG, ad+3)

# Function Set
_[(ad := ad+1)] = ldl(OUT, FUNC_SET)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Display ON/OFF Control
_[(ad := ad+1)] = ldl(OUT, DISP_CNT)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Clear Display
_[(ad := ad+1)] = ldl(OUT, CLS)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# CALL DELAY_2ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_SHORT, ad+3)

# Entry Mode Set
_[(ad := ad+1)] = ldl(OUT, ENT_MOD)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# 1文字ずつ表示する。
_[(ad := ad+1)] = ldl(OUT, ord('h'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('e'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('o'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord(','))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord(' '))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('w'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('o'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('r'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('d'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)
                           
_[(ad := ad+1)] = jmp(ad+1)


generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2554.JPG

4.9 レジスタの16ビット値をHEX値としてキャラクターLCDに表示する

INポートから読み込んだ値をそのままLCDに出力する。

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_lcd_hex.py
import math

def constrain(val, _min, _max):
    return int(min(max(val, _min), _max))

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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ

TMP0   = r0
TMP1   = r1
OUT    = r2
VAL    = r5
RET_TO = r7

# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret(): return jmp_reg(RET_TO)

# カーソル位置(0ベース)をセットする命令
def set_RC(r, c):
    return ldl(OUT, LEFT_MOST[r] + c)

# 0xABCD (0d43981)を0xDCBA (0d56506)に変換する函数
def reverse_4nibles(two_bytes):
    return eval("0x" + ((str(hex(two_bytes))[2:])[::-1]))



FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


F_CPU  = 1E6
PERIOD = 1 / F_CPU


DELAY_40ms  =  40E-3
DELAY_20ms  =  20E-3
DELAY_2ms   =   2E-3
DELAY_100us = 100E-6
DELAY_50us  =  50E-6

LOOPS_for_40ms = (DELAY_40ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_40ms = constrain(LOOPS_for_40ms, 1, 2**16-1)

LOOPS_for_20ms = (DELAY_20ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_20ms = constrain(LOOPS_for_20ms, 1, 2**16-1)

LOOPS_for_2ms = (DELAY_2ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_2ms = constrain(LOOPS_for_2ms, 1, 2**16-1)

LOOPS_for_100us = (DELAY_100us - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_100us = constrain(LOOPS_for_100us, 1, 2**16-1)

LOOPS_for_50us = (DELAY_50us - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_50us = constrain(LOOPS_for_50us, 1, 2**16-1)


FUNC_SET  = 0b111000     # 1, 8/4 bits, 2/1 lines, 10/8 dots, don't care, don't care
DISP_CNT  = 0b1100       # 1, 文字表示, カーソル表示, カーソル点滅
CLS       = 0b1          # 表示を消去
ENT_MOD   = 0b110        # 1, 右/左シフト, 表示範囲全体シフト有効化
LEFT_MOST = [0x80, 0xC0] # y行目x列目のアドレス = (x + y * 0x40) | (0x80)


###############
# subroutines #
###############
ad = 149

# DELAY_40MS:
DELAY_40MS = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_40ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# COMMAND_W:
COMMAND_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b10)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0) 
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DELAY_2MS:
DELAY_2MS = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_2ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DATA_W:
DATA_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b11)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b01)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# VAL_TO_HEX:
VAL_TO_HEX = ad+1
_[(ad := ad+1)] = mov(OUT, VAL)
_[(ad := ad+1)] = ldl(TMP0, 0xF)
_[(ad := ad+1)] = _and(OUT, TMP0)

_[(ad := ad+1)] = ldl(TMP0, 10)             # 値が9より大きいかどうかを判断して、
_[(ad := ad+1)] = mov(TMP1, OUT)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad+5)

_[(ad := ad+1)] = ldl(TMP0, ord('0') )      # 値が9以下であったらそれを文字コードに変換して、
_[(ad := ad+1)] = add(OUT, TMP0)
_[(ad := ad+1)] = jmp(ad+4)

_[(ad := ad+1)] = ldl(TMP0, ord('A') - 0xA) # 値が10以上であったらそれを文字コードに変換して。
_[(ad := ad+1)] = add(OUT, TMP0)
_[(ad := ad+1)] = ret()

# SR_4:
VAL_SR_4 = ad+1
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = ret()

# DELAY_100US:
DELAY_100US = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()


################
# main routine #
################

ad = -1

# 最初にLCDをdisableにする。
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)

# CALL DELAY_40ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_40MS, ad+3)

# Function Set
_[(ad := ad+1)] = ldl(OUT, FUNC_SET)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Display ON/OFF Control
_[(ad := ad+1)] = ldl(OUT, DISP_CNT)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Clear Display
_[(ad := ad+1)] = ldl(OUT, CLS)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# CALL DELAY_2ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_2MS, ad+3)

# Entry Mode Set
_[(ad := ad+1)] = ldl(OUT, ENT_MOD)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)



jump_where = ad+1
# INポートから2バイト値を読み込んで、
_[(ad := ad+1)] = _in(VAL)

# [0]桁目を表示して、
_[(ad := ad+1)] = set_RC(0, 3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3) # ニブル[0]を文字コードに変換して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)     # LCDへ出力して、

# [1]桁目を表示して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_SR_4, ad+3)

_[(ad := ad+1)] = set_RC(0, 2)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3) # ニブル[1]を文字コードに変換して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)     # LCDへ出力して、

# [2]桁目を表示して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_SR_4, ad+3)

_[(ad := ad+1)] = set_RC(0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3) # ニブル[2]を文字コードに変換して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)     # LCDへ出力して、

# [3]桁目を表示して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_SR_4, ad+3)

_[(ad := ad+1)] = set_RC(0, 0)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3)     # ニブル[3]を文字コードに変換して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, jump_where)  # LCDへ出力する。を繰り返す。




generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
IMG_2555.JPG
IMG_2556.JPG

4.10 レジスタの16ビット値を十進数値としてキャラクターLCDに表示する

INポートから読み込んだ値をそのままLCDに出力する。

ROMの初期化ファイル.mifを生成するためのPythonスクリプト:

example_lcd_decimal.py
import math

def constrain(val, _min, _max):
    return int(min(max(val, _min), _max))

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()

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 jmp_reg(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 jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0          # mov(0, 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)


r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ

OUT     = r0
RET_TO  = r1
TMP0    = r2
TMP1    = r3
VAL = r4
Q   = r5 # 商


# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret(): return jmp_reg(RET_TO)

# カーソル位置(0ベース)をセットする命令
def set_RC(r, c):
    return ldl(OUT, LEFT_MOST[r] + c)

# 0xABCD (0d43981)を0xDCBA (0d56506)に変換する函数
def reverse_4nibles(two_bytes):
    return eval("0x" + ((str(hex(two_bytes))[2:])[::-1]))



FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH


F_CPU  = 1E6
PERIOD = 1 / F_CPU


DELAY_40ms  =  40E-3
DELAY_20ms  =  20E-3
DELAY_2ms   =   2E-3
DELAY_100us = 100E-6
DELAY_50us  =  50E-6

LOOPS_for_40ms = (DELAY_40ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_40ms = constrain(LOOPS_for_40ms, 1, 2**16-1)

LOOPS_for_20ms = (DELAY_20ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_20ms = constrain(LOOPS_for_20ms, 1, 2**16-1)

LOOPS_for_2ms = (DELAY_2ms - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_2ms = constrain(LOOPS_for_2ms, 1, 2**16-1)

LOOPS_for_100us = (DELAY_100us - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_100us = constrain(LOOPS_for_100us, 1, 2**16-1)

LOOPS_for_50us = (DELAY_50us - 20 * PERIOD) / (8 * PERIOD)
LOOPS_for_50us = constrain(LOOPS_for_50us, 1, 2**16-1)


FUNC_SET  = 0b111000     # 1, 8/4 bits, 2/1 lines, 10/8 dots, don't care, don't care
DISP_CNT  = 0b1100       # 1, 文字表示, カーソル表示, カーソル点滅
CLS       = 0b1          # 表示を消去
ENT_MOD   = 0b110        # 1, 右/左シフト, 表示範囲全体シフト有効化
LEFT_MOST = [0x80, 0xC0] # y行目x列目のアドレス = (x + y * 0x40) | (0x80)


###############
# subroutines #
###############
ad = 149

# DELAY_40MS:
DELAY_40MS = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_40ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# COMMAND_W:
COMMAND_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b10)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0) 
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DELAY_2MS:
DELAY_2MS = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_2ms - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# DATA_W:
DATA_W = ad+1
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldh(OUT, 0b11)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = nop()
_[(ad := ad+1)] = ldh(OUT, 0b01)
_[(ad := ad+1)] = out(OUT)
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()

# VAL_TO_HEX:
VAL_TO_HEX = ad+1
_[(ad := ad+1)] = mov(OUT, VAL)
_[(ad := ad+1)] = ldl(TMP0, 0xF)
_[(ad := ad+1)] = _and(OUT, TMP0)

_[(ad := ad+1)] = ldl(TMP0, 10)             # 値が9より大きいかどうかを判断して、
_[(ad := ad+1)] = mov(TMP1, OUT)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad+5)

_[(ad := ad+1)] = ldl(TMP0, ord('0') )      # 値が9以下であったらそれを文字コードに変換して、
_[(ad := ad+1)] = add(OUT, TMP0)
_[(ad := ad+1)] = jmp(ad+4)

_[(ad := ad+1)] = ldl(TMP0, ord('A') - 0xA) # 値が10以上であったらそれを文字コードに変換して。
_[(ad := ad+1)] = add(OUT, TMP0)
_[(ad := ad+1)] = ret()

# SR_4:
VAL_SR_4 = ad+1
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = sr(VAL)
_[(ad := ad+1)] = ret()

# DELAY_100US:
DELAY_100US = ad+1
_[(ad := ad+1)] = ldl(TMP0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP1, LOOPS_for_100us - 1)
_[(ad := ad+1)] = sub(TMP1, TMP0)
_[(ad := ad+1)] = jnc(ad)
_[(ad := ad+1)] = ret()


################
# main routine #
################

ad = -1

# 最初にLCDをdisableにする。
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)

# CALL DELAY_40ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_40MS, ad+3)

# Function Set
_[(ad := ad+1)] = ldl(OUT, FUNC_SET)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Display ON/OFF Control
_[(ad := ad+1)] = ldl(OUT, DISP_CNT)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Clear Display
_[(ad := ad+1)] = ldl(OUT, CLS)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# CALL DELAY_2ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_2MS, ad+3)

# Entry Mode Set
_[(ad := ad+1)] = ldl(OUT, ENT_MOD)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)




jump_where = ad+1
_[(ad := ad+1)] = _in(VAL)          # 割られる数(表示する数)をINポートから読み込んで、


# 一の位を取り出して、
_[(ad := ad+1)], \
_[(ad := ad+1)], = ld_hl(TMP1, 10) # 割る数をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(Q, 0)      # 商をリセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)   # 増分をセットして、
_[(ad := ad+1)] = add(Q, TMP0)     # 引いた回数を先にインクリメントしてから、
_[(ad := ad+1)] = sub(VAL, TMP1)   # 実際に引いて、
_[(ad := ad+1)] = jnc(ad-1)        # キャリー(というかボロー)が発生していなければ、また引き算を繰り返すが、
_[(ad := ad+1)] = sub(Q, TMP0)     # キャリー(というかボロー)が発生していたらもう引けないので、1回多く引いたぶんだけ商を戻して、
_[(ad := ad+1)] = add(VAL, TMP1)   # 余りも1回分だけ戻して、

# 一の位を表示して、
_[(ad := ad+1)] = set_RC(0, 4)           # カーソル位置をセットして、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3) # 1の位を文字コードに変換して、
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)     # LCDへ出力して、
_[(ad := ad+1)] = mov(VAL, Q)            # 次に割られる数をセットして、



# 十の位を取り出して、
_[(ad := ad+1)], \
_[(ad := ad+1)], = ld_hl(TMP1, 10)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(Q, 0)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)] = add(Q, TMP0)
_[(ad := ad+1)] = sub(VAL, TMP1)
_[(ad := ad+1)] = jnc(ad-1)
_[(ad := ad+1)] = sub(Q, TMP0)
_[(ad := ad+1)] = add(VAL, TMP1)

# 十の位を表示して、
_[(ad := ad+1)] = set_RC(0, 3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)
_[(ad := ad+1)] = mov(VAL, Q)



# 百の位を取り出して、
_[(ad := ad+1)], \
_[(ad := ad+1)], = ld_hl(TMP1, 10)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(Q, 0)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)] = add(Q, TMP0)
_[(ad := ad+1)] = sub(VAL, TMP1)
_[(ad := ad+1)] = jnc(ad-1)
_[(ad := ad+1)] = sub(Q, TMP0)
_[(ad := ad+1)] = add(VAL, TMP1)

# 百の位を表示して、
_[(ad := ad+1)] = set_RC(0, 2)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)
_[(ad := ad+1)] = mov(VAL, Q)



# 千の位を取り出して、
_[(ad := ad+1)], \
_[(ad := ad+1)], = ld_hl(TMP1, 10)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(Q, 0)
_[(ad := ad+1)], \
_[(ad := ad+1)] = ld_hl(TMP0, 1)
_[(ad := ad+1)] = add(Q, TMP0)
_[(ad := ad+1)] = sub(VAL, TMP1)
_[(ad := ad+1)] = jnc(ad-1)
_[(ad := ad+1)] = sub(Q, TMP0)
_[(ad := ad+1)] = add(VAL, TMP1)

# 千の位を表示して、
_[(ad := ad+1)] = set_RC(0, 1)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)
_[(ad := ad+1)] = mov(VAL, Q)



# 万の位を表示して、
_[(ad := ad+1)] = set_RC(0, 0)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(VAL_TO_HEX, ad+3)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, jump_where) # を繰り返す。


generate_mif(FILE_NAME, WIDTH, DEPTH, _)

実行結果:
INポートから0x89AB (0d35243)を入力している。
IMG_2557.JPG

5. ファイル一式

https://github.com/ti-nspire/MAX_V_MAX10/tree/main/cpu15_mega_ram_symbol

補遺A.

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?