MIPS | 轻量级mips汇编指令转机器指令(Python实现)

当前版本仅支持常用mips汇编指令(在变量r_dicti_dictj_dict中列出),不支持立即数的非十进制输入和非编号型寄存器(即支持用$1, $2等命名的寄存器,但不支持用$v0, $t1等命名的寄存器,如有感兴趣的道友欢迎在reg_dict变量下补全寄存器命名表)。注意,生成的机器指令格式是Quartus软件支持的mif文件格式。

开发环境:python2.7

由于其中部分print语句不符合python3的语法,因此不能在python3下执行。(若注释掉所有的print语句,则应该可以在python3下运行,这个工作量其实并不大)


待修正bug:

  1. 遇到空行时会发生错误【在mips指令文件中不要出现空行即可避免出错】

如果在使用中遇到其他bug欢迎在评论区中指出


为了方便读代码的人理解,我简单解释一下代码的思路:

这个程序是我大二写的,思路比较简单。首先我关注最普通的情况,就是处理一个顺序执行的mips指令的序列,也就是说暂时我不需要管brach指令的tags什么的。在这个假设下,我们遵循一个简单的规则:一条一条地处理mips指令。

现在我们的问题转变到了翻译单条mips指令上。根据我们之前的假设,只要我们能翻译一条指令了,整个指令序列就可以被逐条翻译。对于一条指令,根据教材上的知识我们知道,分为r型,i型和j型,每一种指令型都有自己固定的二进制格式。比如说r型指令翻译成二进制后,一定是000000 $rs $rt $rd $shamt $funct的格式。有了这样的认识,我们就可以分成下面几步翻译单条mips指令:

  • [144行-155行] 把指令按空格拆开成一条一条字符串,比如说add $3 $1 $2拆成['add', '$3', '$1', '$2']
  • [158, 196, 233行] 判断指令的类型,比如我们可以通过'add'指令判断出它属于'r'型
  • [157行-243行] 根据不同的指令类型,把我们第一步拆开得到的字符串序列,按照对应类型的格式拼装成完整的machine code

这样我们就实现了简单的翻译。接下来只需要考虑有tags的情况,比如说有一行我们写的是beq $1, LOOP,下面某一行就是”LOOP“对应的LOOP: add $1 $2 $3。处理这样的情况就要求我们在翻译最开始知道有哪些tags,这个就是程序132-144行做的事情。实现到了这一步,就已经完成了我的程序的功能了。

然后是两个函数,reg2numcomplete

  • reg2num函数的注释写了:convert register name to register number,将寄存器的字符串名称转换为machine code里的数字
  • complete函数的注释里写了:fill a string of length var<width> with binary representation of var<num>,将一个数扩充成指定长度的字符串

这两个函数的实现都非常简单,在此也就不赘述了。


code 如下(修改INFILE变量以读取你自己的mips指令文件,修改out_file.write语句以生成你想要的输出格式)

# file: mif.py
# 2018/06/01 by shawn233
# encoding: utf-8

import copy

INFILE = "mips.txt"
OUTFILE = INFILE.split('.')[0] + ".mif"

tag_dict = {}
r_dict = {'add', 'sub', 'and', 'or', 'xor', 'sll', 'srl', 'sra', 'jr'}
i_dict = {'lw', 'sw', 'beq', 'bne' , 'addi', 'andi', 'ori', 'xori', 'lui'}
j_dict = {'j', 'jal'}

special_r_dict = {'sll', 'srl', 'sra', 'jr'}
shift_r_dict = {'beq', 'bne', 'sll', 'srl', 'sra'}

special_i_dict = {'beq', 'bne', 'lw', 'sw', 'lui'}
branch_i_dict = {'beq', 'bne'}

op_code = { 'addi':'001000',
            'andi':'001100',
            'ori' :'001101',
            'xori':'001110',
            'lw'  :'100011',
            'sw'  :'101011',
            'beq' :'000100',
            'bne' :'000101',
            'lui' :'001111',
            'j'   :'000010',
            'jal' :'000011' }

funct = {'add':'100000',
            'sub':'100010',
            'and':'100100',
            'or' :'100101',
            'xor':'100110',
            'sll':'000000',
            'srl':'000010',
            'sra':'000011',
            'jr' :'001000'}

reg_dict = {
        '$0':0,
        '$1':1,
        '$2':2,
        '$3':3,
        '$4':4,
        '$5':5,
        '$6':6,
        '$7':7,
        '$8':8,
        '$9':9,
        '$10':10,
        '$11':11,
        '$12':12,
        '$13':13,
        '$14':14,
        '$15':15,
        '$16':16,
        '$17':17,
        '$18':18,
        '$19':19,
        '$20':20,
        '$21':21,
        '$22':22,
        '$23':23,
        '$24':24,
        '$25':25,
        '$26':26,
        '$27':27,
        '$28':28,
        '$29':29,
        '$30':30,
        '$31':31,
        '$ra':8
}

def complete (num, width):
    '''
    fill a string of length var<width> with binary representation of var<num>
    '''
    format_ = '{0:0>'+str(width)+'b}'
    if (num >= 0):
        return format_.format(num)
    else:
        return bin(num & int('1' * width, 2))

def bin2hex (bin_form):
    '''
    convert binary string to corresponding hexadecimal string
    '''
    format_ = '{0:1x}'
    hex_form = ''
    for i in xrange(0, 32, 4):
        hex_form += format_.format(int(bin_form[i: i+4], base=2))

    return hex_form

def reg2num(reg):
    '''
    convert register name to register number
    '''
    return reg_dict[reg.strip()]

def main():
    print OUTFILE
    in_file = open(INFILE, 'r')
    mips_ = in_file.readlines()
    in_file.close()

    out_file = open(OUTFILE, 'w')

    code = None
    machine_code = None
    op = None
    regs = None
    rs = None
    rt = None
    rd = None
    sa = None
    imm = None
    line_num_format = '{0:x}'

    mips = copy.deepcopy(mips_)

    # clear comments
    for line in xrange(len(mips)):
        code = mips[line].strip().split('#')
        mips[line] = code[0].strip()

    # save tag
    for line in xrange(len(mips)):
        code = mips[line].strip().split(":")
        if (len(code) == 2):
            tag_dict[code[0].strip()] = line
            mips[line] = code[1].strip()
        elif (len(code) > 2):
            print "non-standard mips instruction encountered in line", line, "code:", mips[line]
            exit()

    # conversion
    for line in xrange(len(mips)):
        code = mips[line].strip()
        whitespace_ind = code.find(' ')
        op = code[:whitespace_ind].strip()
        regs = code[whitespace_ind:].strip().split(',')

        #raw_input()
        print
        print "code", code
        print "whitepace_ind", whitespace_ind
        print "op", op
        print "regs", regs
        #raw_input()

        machine_code = ''
        if op in r_dict:
            # 000000 $rs $rt $rd $shamt $funct
            machine_code += ('0' * 6)
            if op in special_r_dict:
                if op in shift_r_dict:
                    try:    
                        rd = reg2num(regs[0])
                        rs = 0
                        rt = reg2num(regs[1])
                        sa = int (regs[2].strip())
                    except Exception, e:
                        print "Exception encountered in r-type conversion |", e
                        #exit()
                else: # jr
                    try:
                        rd = 0
                        rs = reg2num(regs[0])
                        rt = 0
                        sa = 0
                    except Exception, e:
                        print "Exception encountered in r-type conversion |", e
                        #exit()    
            else:
                try:
                    rd = reg2num(regs[0])
                    rs = reg2num(regs[1])
                    rt = reg2num(regs[2])
                    sa = 0
                except Exception, e:
                    print "Exception encountered in r-type conversion |", e
                    #exit()
            print "rs:", rs, "type:", type(rs)
            print "rt:", rt, "type:", type(rt)
            print "rd:", rd, "type:", type(rd)
            print "sa:", sa, "type:", type(sa)
            machine_code += (complete(rs, 5)+ complete(rt, 5)+ complete(rd, 5) + complete(sa, 5))
            machine_code += funct[op]
        
        elif op in i_dict:
            machine_code += op_code[op]
            if op in special_i_dict:
                if op in branch_i_dict:
                    try:
                        rs = reg2num(regs[0])
                        rt = reg2num(regs[1])
                        imm = tag_dict[regs[2].strip()] - line - 1
                    except Exception, e:
                        print "Exception encountered in i-type conversion |", e
                elif op == 'lui':
                    try:
                        rs = 0
                        rt = reg2num(regs[0])
                        imm = int(regs[1].strip())
                    except Exception, e:
                        print "Exception encountered in i-type conversion |", e
                else: # sw lw
                    try:
                        rt = reg2num(regs[0])
                        regs[1] = regs[1].strip()
                        pos1 = regs[1].find('(')
                        pos2 = regs[1].find(')')
                        imm = int(regs[1][:pos1].strip())
                        rs = reg2num(regs[1][pos1+1:pos2])
                    except Exception, e:
                        print "Exception encountered in i-type conversion |", e
                
            else:
                try:
                    rt = reg2num(regs[0])
                    rs = reg2num(regs[1])
                    imm = int(regs[2].strip())
                except Exception, e:
                    print "Exception encountered in i-type conversion |", e
            machine_code += (complete(rs, 5)+complete(rt, 5)+complete(imm, 16))
        
        elif op in j_dict:
            machine_code += op_code[op]
            try:
                imm = int(tag_dict[regs[0].strip()])
            except Exception, e:
                print "Exception encountered in j-type conversion |", e
            machine_code += complete(imm, 26)
            
        else:
            print "unknown instruction encountered in line", line, "code:", mips[line]
            exit()
        out_file.write(line_num_format.format(line) + ' : ' + bin2hex(machine_code) + ';  % ' + mips_[line].strip('\n') + ' | ' + machine_code + ' %' + '\n')

    out_file.close()

if __name__ == "__main__":
    main()
    #print complete(-1, 5)

Appendix

附上我的mips和翻译之后的mif文件

mips:

main:   addi $4, $0, 1
        addi $5, $0, 2
        addi $6, $0, 3
loop:   lw $1, 0($0)
        lw $2, 4($0)
        lw $3, 8($0)
        beq $3, $0, add
        beq $3, $4, sub
        beq $3, $5, and
        beq $3, $6, or
        j loop
add:    add $7, $1, $2
        j show
sub:    sub $7, $2, $1
        j show
and:    and $7, $1, $2
        j show
or:     or $7, $1, $2
show:   sw $1, 0($0)
        sw $2, 4($0)
        sw $3, 8($0)
        j loop

mif:

0 : 20040001;  % main:   addi $4, $0, 1 | 00100000000001000000000000000001 %
1 : 20050002;  %         addi $5, $0, 2 | 00100000000001010000000000000010 %
2 : 20060003;  %         addi $6, $0, 3 | 00100000000001100000000000000011 %
3 : 8c010000;  % loop:   lw $1, 0($0) | 10001100000000010000000000000000 %
4 : 8c020004;  %         lw $2, 4($0) | 10001100000000100000000000000100 %
5 : 8c030008;  %         lw $3, 8($0) | 10001100000000110000000000001000 %
6 : 10600004;  %         beq $3, $0, add | 00010000011000000000000000000100 %
7 : 10640005;  %         beq $3, $4, sub | 00010000011001000000000000000101 %
8 : 10650006;  %         beq $3, $5, and | 00010000011001010000000000000110 %
9 : 10660007;  %         beq $3, $6, or | 00010000011001100000000000000111 %
a : 08000003;  %         j loop | 00001000000000000000000000000011 %
b : 00223820;  % add:    add $7, $1, $2 | 00000000001000100011100000100000 %
c : 08000012;  %         j show | 00001000000000000000000000010010 %
d : 00413822;  % sub:   sub $7, $2, $1 | 00000000010000010011100000100010 %
e : 08000012;  %        j show | 00001000000000000000000000010010 %
f : 00223824;  % and:   and $7, $1, $2 | 00000000001000100011100000100100 %
10 : 08000012;  %       j show | 00001000000000000000000000010010 %
11 : 00223825;  % or:       or $7, $1, $2 | 00000000001000100011100000100101 %
12 : ac010000;  % show: sw $1, 0($0) | 10101100000000010000000000000000 %
13 : ac020004;  %       sw $2, 4($0) | 10101100000000100000000000000100 %
14 : ac030008;  %       sw $3, 8($0) | 10101100000000110000000000001000 %
15 : 08000003;  %       j loop | 00001000000000000000000000000011 %

Q&A

有同学问到special/shift_r_dictspecial/branch_i_dict这四个分类的依据。其实这个问题问得稍有不妥,因为special_r_dictshift_r_dict并不是并列的分类,实际上是不同层级的一个分类;special_i_dictbrach_i_dict同理。

special_r_dict区别于r_dict - special_r_dict中的指令(减号表示差集)。普通的r型指令,如add $1, $2, $3;特殊(special)的r型指令,如sll $1, $2, 3。对比之下不难发现,对于r型指令的machine code格式

# 000000 $rs $rt $rd $shamt $funct

普通的r型指令填充的是$rs, $rt$rd三个位置,而特殊的r型指令填充的则不是。因此在实现时,这两种类型的指令需要分开来写。

i_dictspecial_i_dict同理,用于区分普通的i型指令和特殊的i型指令。

special_r_dictshift_r_dict同理,用于区分包含shift和不包含shift(可以看代码,不包含的是jr指令)的指令。special_i_dictbranch_i_dict同理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351