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)を作る
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スクリプトで生成する。
生成された.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)を作る
2.4 レジスタへの書き込み部(reg_wb)を作る
8本の16ビットレジスタのうち1本を選んで書き込む。
2.4.1 まず16ビットレジスタを1個作る
2.4.2 それを8個連結する
2.5 RAMの読み出し/書き込み部(mega_ram)を作る
IP CatalogにあるRAM: 1-PORTを使って16ビット × 64本のRAMを作る。RAM: 1-PORTの使い方はROM: 1-PORTと同じ。
2.6 演算部(exec)を作る
2.6.1 プログラムカウンターを作る
2.6.2 加減算器を作る
追加したjnc
命令用にキャリーを得たいので加減算自体は17ビットで行う。
2.6.2.1 2の補数回路を作る
2.6.2.2 組み合わせて加減算器にする
2.6.3 全部組み合わせてexec部を作る
一部命令を差し替えたため、加減算器からキャリーを取り出している点(jnc
命令用)と、プログラムカウンターのロード値が選べるようになっている点(jmp_reg
命令用)とがテキストと違っている。
2.7 すべてのコンポーネントを組み合わせてCPUを作る
2.8 ソースクロック生成部を作る
IP Catalogに登録されているALTPLLを利用する。ここでは、評価ボードに載っている48 MHz発振器から1 MHzを合成した。1 MHzにした理由は特にない。ALTPLLの使いかたはhttps://ti-nspire.hatenablog.com/entry/2021/01/25/071741を参照。
3. 端子を割り当てる
下のように割り当てた。評価ボード上の48 MHz発振器がMAX10 (10M08SAE144C8G)の27番ピンに接続してある。コンパイルする前に.mifファイルをQuartus Primeのプロジェクトファイルと同じディレクトリに入れておく。
(↑この図は、『MAX10実験キットで学ぶFPGA&コンピュータ』附録ディスクに収録されているMAX10-FB_Document_Rev03_20150911.pdfから)
4. サンプルプログラム
4.1 RAMのテストをする
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.2 1から361までの総和を求める
sigma(n, n=1 to 361) = 65341 (0b 1111 1111 0011 1101)
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.3 掛け算
300 * 200 = 60000 (0b 1110 1010 0110 0000)
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.4 割り算
65000 ÷ 51 = 1274 (0b 0000 0100 1111 1010)
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.5 Lチカ
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.6 Lチカ(ビジーループをサブルーチンっぽくする)
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.7 ナイトライダー
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.8 キャラクターLCDでhello, world
3.3 Vで直接動かせるSC1602BBWB-XA-LB-Gを使った。
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.9 レジスタの16ビット値をHEX値としてキャラクターLCDに表示する
INポートから読み込んだ値をそのままLCDに出力する。
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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, _)
4.10 レジスタの16ビット値を十進数値としてキャラクターLCDに表示する
INポートから読み込んだ値をそのままLCDに出力する。
ROMの初期化ファイル.mifを生成するためのPythonスクリプト:
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)を入力している。
5. ファイル一式
https://github.com/ti-nspire/MAX_V_MAX10/tree/main/cpu15_mega_ram_symbol
補遺A.