レジスタに入っている16ビットデータを4桁HEX値としてキャラクターLCDに表示する

f:id:ti-nspire:20210218141510j:plain:w700

############################
# アセンブラ(のようなもの) #
############################

import math

def constrain(val, _max, _min):
    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 sra(ra):       return  7 << 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 hlt():         return 15 << 11
def jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0
def _in(ra): return ld(ra, 65) # INポートからレジスタへ読み込む。
def out(ra): return st(ra, 64) # レジスタからOUTポートへ書き出す。


# レジスタへ2バイト即値をロード(★2命令★)
def ld_hl(ra, two_bytes):
    return ldh(ra, two_bytes >> 8), \
           ldl(ra, two_bytes & 0xFF)


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



################################################
########### アセンブリ(のようなもの) ###########
################################################

##################################
# directive、macro(のようなもの) #
##################################

F_CPU = 10E3

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

# (3命令×4クロック + 2命令×4クロック×(LOOPS定数 + 1) + 1命令×4クロック) / F_CPU = delay
# LOOPS定数 = (delay * F_CPU - 16) / 8 - 1
LOOPS_for_40ms = constrain(math.ceil((DELAY_40ms   * F_CPU - 16) / 8 - 1), 2**16-1, 0) # F_CPU = 10E6のとき49997
LOOPS_for_2ms  = constrain(math.ceil((DELAY_2ms    * F_CPU - 16) / 8 - 1), 2**16-1, 0)
LOOPS_for_100us = constrain(math.ceil((DELAY_100us * F_CPU - 16) / 8 - 1), 2**16-1, 0)

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)


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

VAL = 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]))


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

# DELAY_40MS:
DELAY_40MS = 245 # ~250
_[(pc := DELAY_40MS)] = ldl(TMP0, 1)
_[(pc := pc+1)], \
_[(pc := pc+1)]       = ld_hl(COUNTER, LOOPS_for_40ms)
_[(pc := pc+1)]       = sub(COUNTER, TMP0)
_[(pc := pc+1)]       = jnc(pc)
_[(pc := pc+1)]       = ret()

# COMMAND_W:
COMMAND_W = 230 # ~243
_[(pc := COMMAND_W)] = out(OUT)
_[(pc := pc+1)]      = ldh(OUT, 0b00)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = ldh(OUT, 0b10)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = nop()
_[(pc := pc+1)]      = ldh(OUT, 0b00)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = ldl(TMP0, 1) # ビジーループ
_[(pc := pc+1)], \
_[(pc := pc+1)]      = ld_hl(COUNTER, LOOPS_for_100us)
_[(pc := pc+1)]      = sub(COUNTER, TMP0) 
_[(pc := pc+1)]      = jnc(pc)
_[(pc := pc+1)]      = ret()

# DELAY_2MS:
DELAY_2MS = 220 # ~225
_[(pc := DELAY_2MS)] = ldl(TMP0, 1)
_[(pc := pc+1)], \
_[(pc := pc+1)]        = ld_hl(COUNTER, LOOPS_for_2ms)
_[(pc := pc+1)]        = sub(COUNTER, TMP0)
_[(pc := pc+1)]        = jnc(pc)
_[(pc := pc+1)]        = ret()

# DATA_W:
DATA_W = 205 # ~216
_[(pc := DATA_W)] = out(OUT)
_[(pc := pc+1)]   = ldh(OUT, 0b11)
_[(pc := pc+1)]   = out(OUT)
_[(pc := pc+1)]   = nop()
_[(pc := pc+1)]   = ldh(OUT, 0b01)
_[(pc := pc+1)]   = out(OUT)
_[(pc := pc+1)]   = ldl(TMP0, 1) # ビジーループ
_[(pc := pc+1)], \
_[(pc := pc+1)]   = ld_hl(COUNTER, LOOPS_for_100us)
_[(pc := pc+1)]   = sub(COUNTER, TMP0)
_[(pc := pc+1)]   = jnc(pc)
_[(pc := pc+1)]   = ret()

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

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

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

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

# SR_4:
VAL_SR_4 = 185 # ~189
_[(pc := VAL_SR_4)] = sr(VAL)
_[(pc := pc+1)]     = sr(VAL)
_[(pc := pc+1)]     = sr(VAL)
_[(pc := pc+1)]     = sr(VAL)
_[(pc := pc+1)]     = ret()

# DELAY_100US:
DELAY_100US = 175 # ~180
_[(pc := DELAY_100US)] = ldl(TMP0, 1)
_[(pc := pc+1)], \
_[(pc := pc+1)]        = ld_hl(COUNTER, LOOPS_for_100us)
_[(pc := pc+1)]        = sub(COUNTER, TMP0)
_[(pc := pc+1)]        = jnc(pc)
_[(pc := pc+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)



RETURN_HERE = 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, RETURN_HERE)  # LCDへ出力する。を繰り返す。





_[255] = jmp(255) # unreachable

generate_mif(FILE_NAME, WIDTH, DEPTH, _)