Created
April 27, 2016 17:26
-
-
Save long-long-float/a398cc5c1b9798da62fa33bd1c2c825a to your computer and use it in GitHub Desktop.
titech情報科学科の機械語シュミレータのトランスレータ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ライフゲームのプログラム。かなり遅い。 | |
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