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

# 1.　概要
『[動かしてわかるCPUの作り方10講](https://gihyo.jp/book/2019/978-4-297-10821-2)』\(井澤裕司、技術評論社\)第9講までのCPUをFPGA MAX10 + 回路図エディターで作る。

MAX10 \(10M08SAE144C8G\)の評価ボードは[マルツ製](https://www.marutsu.co.jp/pc/i/598579/)を使った\(参考: 『[MAX10実験キットで学ぶFPGA&コンピュータ](https://shop.cqpub.co.jp/hanbai/books/48/48071.html)』\(圓山宗智、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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/af47a1ce-a23c-7118-6ec6-9776bd6dce58.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/57df8c77-7455-0d83-207d-91eac0cd4c18.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](https://ti-nspire.hatenablog.com/entry/2021/01/28/063115)を参照。<font color="Red">ROMの初期化ファイルである.mifファイルは、あとの「[4. サンプルプログラム](https://qiita.com/cyq04000/items/db60e7e08929115f1d2e#4%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0)」のなかで紹介するPythonスクリプトで生成する</font>。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/b4d4dcac-a28e-e436-2c26-e1aed3be41f6.png)


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

```vhdl
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/d4bceeb9-fb49-4a41-85c4-12230045d183.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/25d38c19-dacb-6672-bcaf-c629b4f2063c.png)

## 2.4　レジスタへの書き込み部\(reg_wb\)を作る
8本の16ビットレジスタのうち1本を選んで書き込む。

### 2.4.1　まず16ビットレジスタを1個作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/599c7848-ef07-feaa-17f7-272a129b23c5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/dfb2dbba-aa33-a167-fdf3-f97638a5fcf5.png)

### 2.4.2　それを8個連結する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/d19272e0-8746-dd80-16a4-07d3d05eaed5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/55009002-54ca-a917-83c0-070160b75665.png)


## 2.5　RAMの読み出し/書き込み部\(mega_ram\)を作る
IP CatalogにあるRAM: 1-PORTを使って16ビット × 64本のRAMを作る。RAM: 1-PORTの使い方はROM: 1-PORTと同じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/8207bb18-9a31-b830-b7ba-f45ff17d8f12.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/57b677d9-1c7f-6bb8-1f0d-d51a9034d947.png)

## 2.6　演算部\(exec\)を作る


### 2.6.1　プログラムカウンターを作る
sclr、sloadのsはsynchronous。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/b40e3cd4-f47a-3aae-9557-89aa7f74e299.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/6ac26be6-4d2f-9c70-149c-adac639879d5.png)

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

#### 2.6.2.1　2の補数回路を作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/1a4bc517-40d5-a3f3-208d-4940df35c232.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/cc5866c9-75c2-2408-552f-8b366302b955.png)

#### 2.6.2.2　組み合わせて加減算器にする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/cba106ac-4543-b2c8-deca-f7aa75f0248f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/993129ef-f143-5de3-38d1-efe3ba2e0a8f.png)

### 2.6.3　全部組み合わせてexec部を作る
一部命令を差し替えたため、加減算器からキャリーを取り出している点(`jnc`命令用)と、プログラムカウンターのロード値が選べるようになっている点(`jmp_reg`命令用)とがテキストと違っている。    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/d9c36912-ccd4-57d4-846b-8b3a53229d97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/1f7db98f-32ad-4a6b-41ae-24f960bfb20c.png)

## 2.7　すべてのコンポーネントを組み合わせてCPUを作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/c1fa34f0-9f18-76ba-3848-ca56cbd2b87a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/0f9f9574-2118-8bec-87a1-7cc1d242e619.png)

## 2.8　ソースクロック生成部を作る
IP Catalogに登録されているALTPLLを利用する。ここでは、[評価ボード](https://www.marutsu.co.jp/pc/i/598579/)に載っている[48 MHz発振器](https://akizukidenshi.com/catalog/g/gP-03939/)から1 MHzを合成した。1 MHzにした理由は特にない。ALTPLLの使いかたは[https://ti-nspire.hatenablog.com/entry/2021/01/25/071741](https://ti-nspire.hatenablog.com/entry/2021/01/25/071741)を参照。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/330ac1cc-0be5-3b31-756b-a04c0bd5a64d.png)

# 3.　端子を割り当てる
下のように割り当てた。[評価ボード](https://www.marutsu.co.jp/pc/i/598579/)上の[48 MHz発振器](https://akizukidenshi.com/catalog/g/gP-03939/)がMAX10 \(10M08SAE144C8G\)の27番ピンに接続してある。コンパイルする前に.mifファイルをQuartus Primeのプロジェクトファイルと同じディレクトリに入れておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/18bad6a1-3752-d4db-b66a-421621137d54.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/572b27da-3ded-3b65-57e0-8ed809fc5b01.png)
\(↑この図は、『[MAX10実験キットで学ぶFPGA&コンピュータ](https://shop.cqpub.co.jp/hanbai/books/48/48071.html)』附録ディスクに収録されているMAX10-FB_Document_Rev03_20150911.pdfから\)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/1acd0e6e-6c54-99a7-4f35-9a3bcee59e0e.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/336a479d-cc8d-0b07-5f64-05a4842faff6.jpeg)

## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/d5a1608f-a02f-4218-14f9-3825c9669471.jpeg)




## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/6e91c7ce-0792-94fa-e025-f6cec140aef0.jpeg)


## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/b35649fa-10cf-53fe-9a97-74c8c8229f01.jpeg)


## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/c6ae4b08-f31f-6fc9-f794-f89a30748bd2.png)](https://youtu.be/pAz0Z3tMfz4)



## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/11cf4a9e-495e-3755-77cb-9bde94d0fc90.png)](https://youtu.be/cxeJ7O7czvw)



## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/a8cf8178-bc9c-efda-a24b-b2d48065a8df.png)](https://youtu.be/TFGBQXf5qe0)



## 4.8　キャラクターLCDでhello, world
3.3 Vで直接動かせる[SC1602BBWB-XA-LB-G](https://akizukidenshi.com/catalog/g/gP-04794/)を使った。

CPUとLCDとの接続は下のとおりとした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/cd3fa8e1-cd86-8aea-22c1-c2c760c32568.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/904d97d6-3e53-c89b-6ea2-b423ffa77803.jpeg)



## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/aae05820-008d-0362-ffbf-4d52e112ea04.jpeg)
![IMG_2556.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/0e6ce6a5-65e9-a3ce-3dd5-f5ca6dce170d.jpeg)


## 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/c8ff669b-db4f-bf56-62b3-a3d7ee51bf68.jpeg)


# 5.　ファイル一式
[https://github.com/ti-nspire/MAX_V_MAX10/tree/main/cpu15_mega_ram_symbol](https://github.com/ti-nspire/MAX_V_MAX10/tree/main/cpu15_mega_ram_symbol)

# 補遺A.
https://ti-nspire.hatenablog.com/entry/2021/09/17/135507?_ga=2.76261525.1243545954.1620164649-51586658.1596933926