Skip to content

Instantly share code, notes, and snippets.

@long-long-float
Created April 27, 2016 17:26
Show Gist options
  • Save long-long-float/a398cc5c1b9798da62fa33bd1c2c825a to your computer and use it in GitHub Desktop.
Save long-long-float/a398cc5c1b9798da62fa33bd1c2c825a to your computer and use it in GitHub Desktop.
titech情報科学科の機械語シュミレータのトランスレータ
# Usage: ruby generator.rb FILE
class MLS < BasicObject
attr_reader :ops
def initialize
@ops = []
@addr = 0
@labels = {}
@label_count = 0
end
def generate
@ops << ['P', int2bin(PROGRAM_START)]
@ops.map do |op|
op.map do |token|
if token.is_a? ::Label
int2bin(@labels[token.id])
else
token.split(' ').join
end
end.join(',')
end.join("\n")
end
private
PROGRAM_START = 1 << 4
# comment
def c(content)
@ops << [content]
end
def data(&block)
_addr = @addr
@addr = 0
instance_eval(&block)
@addr = _addr
end
def program(&block)
_addr = @addr
@addr = PROGRAM_START
instance_eval(&block)
@addr = _addr
end
def mem(addr = nil, data1, data2)
add_op([data1, data2], addr)
end
def op(op1, op2)
add_op([op1, op2])
end
def label(l)
@labels[l.id] = @addr
end
def _for(register, b, e)
forb = mk_label
fore = mk_label
nl = mk_label
reg = r(register)
# initialize
_load_imm register, b
# start for
label forb
# rAを比較用に用いる
if e.is_a? ::Symbol
_copy :A, e
else
_load_imm :A, e
end
op "111 10 #{reg}", '00000000'
op '001 01 000', fore
yield(nl, fore)
label nl
#register++
op "110 00 #{reg}", '00000000'
op '001 00 000', forb
# end for
label fore
end
def _loop
loopb = mk_label
loope = mk_label
label loopb
yield(loopb, loope)
op '001 00 000', loopb
label loope
end
def _if(iftrue, ifelse = nil)
elses = mk_label
elsee = mk_label
op '001 10 000', elses
iftrue.call
op '001 00 000', elsee
label elses
if ifelse
ifelse.call
end
label elsee
end
def _continue_if(nl)
op '001 01 000', nl
end
alias :_break_if :_continue_if
def _noop
op '111 00 000', '00000000'
end
def _load(register, addr)
op "010 00 #{r(register)}", addr
end
def _loadr(reg, pointer)
op "010 01 #{r(reg)}", r(pointer, 8)
end
def _load_imm(register, val)
_zero register
op "011 01 #{r(register)}", i(val)
end
def _storer(val, pointer)
op "010 11 #{r(val)}", r(pointer, 8)
end
def _store(addr, val)
op "010 10 #{r(val)}", addr
end
def _zero(register)
op "011 00 #{r(register)}", i(0)
end
def _copy(dest, src)
op "011 10 #{r(dest)}", r(src, 8)
end
def _rshift(register, n)
op "100 10 #{r(register)}", i(n)
end
def _lshift(register, n)
op "100 00 #{r(register)}", i(n)
end
def _lrot(register, n)
op "100 01 #{r(register)}", i(n)
end
def _not(reg)
op "101 00 #{r(reg)}", i(0)
end
def _and(dest, src1, src2)
op "101 01 #{r(dest)}", "#{r(src1, 4)} #{r(src2, 4)}"
end
def _or(dest, src1, src2)
op "101 10 #{r(dest)}", "#{r(src1, 4)} #{r(src2, 4)}"
end
def _xor(dest, src1, src2)
op "101 11 #{r(dest)}", "#{r(src1, 4)} #{r(src2, 4)}"
end
def _inc(reg)
op "110 00 #{r(reg)}", i(0)
end
def _sum(dest, src1, src2)
op "110 10 #{r(dest)}", "#{r(src1, 4)} #{r(src2, 4)}"
end
def _sub(dest, src1, src2)
op "110 11 #{r(dest)}", "#{r(src1, 4)} #{r(src2, 4)}"
end
def _zero?(reg)
op "111 00 #{r(reg)}", i(0)
end
def mk_label
::Label.new(@label_count += 1)
end
def add_op(ops, addr = nil)
@ops << ['M', addr || int2bin(@addr), *ops]
@addr += 1
end
def int2bin(val, len = 8)
"%0#{len}d" % val.to_s(2)
end
alias :i :int2bin
def r2i(name)
[*:A..:H].index(name)
end
# register
def r(name, len = 3)
int2bin(r2i(name), len)
end
end
class Label
attr_reader :id
def initialize(id)
@id = id
end
end
mls = MLS.new
mls.instance_eval(ARGF.read, ARGF.filename, 1)
puts mls.generate
# ライフゲームのプログラム。かなり遅い。
height = 4
width = 16
MASK_ADDR = '11100000'
BITCNT55_ADDR = '11100001'
BITCNT33_ADDR = '11100010'
BITCNT0F_ADDR = '11100011'
BITCNT3F_ADDR = '11100100'
MAX_ROW = '11100101'
CENTER_MASK = '11100111'
c '画面'
data do
mem '00100000', '00100000'
mem '00100000', '00011000'
mem '00100000', '01100000'
mem '00000000', '00010000'
mem MASK_ADDR, '10000000', '00000011'
mem BITCNT55_ADDR, '01010101', '01010101'
mem BITCNT33_ADDR, '00110011', '00110011'
mem BITCNT0F_ADDR, '00001111', '00001111'
mem BITCNT3F_ADDR, '00000000', '00001111'
mem MAX_ROW, i(0), i(width - 1 + 2)
mem CENTER_MASK, i(0), i(1)
end
c 'プログラム'
program do
# rA: 一時計算用
# rB: 行のカウンタ
# rC: 列のカウンタ
# rD: マスク
# rE: ライン(3行分) -> ビットの数
# rF: 結果
# rG: ビットカウントの行のカウンタ
# rH: メモリからの一時ロード用
c '画面からメモリにロードする'
start = mk_label
label start
_for :B, 0..height do
_load :D, MASK_ADDR
_zero :F
_for :C, 0..width do
c 'ルールに従って生存・死滅させる'
_zero :E
c '8近傍のビットをとる'
_for :G, 0..3 do |nl, bl|
_sum :A, :B, :G
# 上下は0だと仮定し、判定はしない
c 'ラインをマスクする'
op "110 01 #{r(:A)}", i(0) # rA = rB + rG - 1
op "010 01 #{r(:A)}", r(:A, 8) # rA = *(rA)
_and :A, :A, :D
_or :E, :E, :A
_lrot :E, 3
end
c 'ビットの数を数える 参考: http://www.mwsoft.jp/programming/java/java_lang_integer_bit_count.html'
c 'E -= (E >> 1) & *BITCNT55_ADDR'
_copy :A, :E
_rshift :A, 1
_load :H, BITCNT55_ADDR
_and :A, :A, :H
_sub :E, :E, :A
c 'E = (E & *BITCNT33_ADDR) + ((E >> 2) & *BITCNT33_ADDR)'
_copy :A, :E
_load :H, BITCNT33_ADDR
_and :E, :E, :H
_rshift :A, 2
_and :A, :A, :H
_sum :E, :E, :A
c 'E = (E + (E >> 4)) & *BITCNT0F_ADDR'
_copy :A, :E
_rshift :A, 4
_sum :E, :E, :A
_load :H, BITCNT0F_ADDR
_and :E, :E, :H
c 'E = E + E >> 8'
_copy :A, :E
_rshift :A, 8
_sum :E, :E, :A
c 'E &= 0x3f'
_load :H, BITCNT3F_ADDR
_and :E, :E, :H
_load :H, CENTER_MASK
op "010 01 #{r(:A)}", r(:B, 8) # rE = *(rB)
_and :A, :A, :H
op "111 00 #{r(:A)}", i(0)
_if -> do
c '死んでいるセル'
_load_imm :A, 3
op "111 01 #{r(:E)}", r(:A, 8)
_if -> do
c '誕生'
_or :F, :F, :H
end
end, -> do
c '生きているセル'
op "110 01 #{r(:E)}", i(0)
_load_imm :A, 1
op "111 10 #{r(:A)}", r(:E, 8)
_if -> do
c '過疎'
end, -> do
_load_imm :A, 4
op "111 10 #{r(:E)}", r(:A, 8)
_if -> do
c '過密'
end, -> do
c '生存'
_or :F, :F, :H
end
end
end
_lrot :H, 1
_store CENTER_MASK, :H
_lrot :D, 1
end
_load_imm :A, 0xf0
_sum :A, :B, :A
_storer :F, :A
end
c 'メモリから画面に書き出す'
_for :B, 0..height do
op '011 01 000', '11110000'
op "110 10 #{int2bin(6, 3)}", '0001 0000'
op '010 01 101', "00000#{int2bin(6, 3)}"
op '010 11 101', i(1)
end
op '001 00 000', start
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment