├── .gitignore ├── README.md ├── manual ├── README.md └── manual.pdf ├── screenshot ├── assemble.png ├── pseudo.png └── simulate.png ├── src ├── com │ └── tsreaper │ │ └── yame │ │ ├── assemble │ │ ├── assemble_exception.py │ │ ├── assembler.py │ │ ├── data.py │ │ ├── immediate.py │ │ ├── immediate_register.py │ │ ├── instruction.py │ │ ├── label.py │ │ ├── pseudo.py │ │ ├── register.py │ │ └── string.py │ │ ├── constant │ │ ├── config.py │ │ ├── instruction_template.py │ │ ├── register_list.py │ │ └── token_regex.py │ │ ├── disassemble │ │ ├── data.py │ │ ├── disassemble_exception.py │ │ ├── disassembler.py │ │ ├── instruction.py │ │ └── register.py │ │ ├── io │ │ ├── bin_file.py │ │ ├── coe_file.py │ │ └── file_exception.py │ │ ├── simulate │ │ ├── simulation_exception.py │ │ ├── simulation_info_exception.py │ │ └── simulator.py │ │ └── ui │ │ ├── bin_browser.py │ │ ├── main_window.py │ │ ├── message_browser.py │ │ ├── mips_editor.py │ │ ├── mips_highlighter.py │ │ ├── pseudo_dialog.py │ │ ├── reg_browser.py │ │ ├── setting_dialog.py │ │ ├── simulator_window.py │ │ ├── terminal_browser.py │ │ └── terminal_input.py ├── config.cfg ├── icons │ ├── reset.png │ ├── run.png │ ├── step.png │ ├── yame.ico │ └── yame.png └── main.pyw └── test ├── guess.asm └── sum.asm /.gitignore: -------------------------------------------------------------------------------- 1 | *.bat 2 | *.pyc 3 | __pycache__ 4 | bin 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YAME: A MIPS Editor 2 | 3 | 4 | YAME 是一个简单的 MIPS 编辑器 / 汇编器 / 反汇编器 / 模拟器,并带有图形界面。它是我为浙江大学《计算机组成》课程(教师:LXQ)编写的课程项目。 5 | 6 | YAME 是 YAME: A MIPS Editor 的缩写,也可以认为是 Yet Another MIPS Editor 的缩写。 7 | 8 | ## 预览 9 | assemble 10 | 11 | pseudo 12 | 13 | simulate 14 | 15 | ## 特性 16 | - 支持语法高亮; 17 | - 支持自定义伪指令; 18 | - 支持 .asm 汇编文件汇编为 .coe 文件或 .bin 文件; 19 | - 支持 .coe 文件或 .bin 文件反汇编为 .asm 汇编文件汇编为; 20 | - 支持模拟运行,支持模拟终端输入输出; 21 | - 支持简单的调试功能(单步运行、全部运行、查看寄存器与内存的值)。 22 | 23 | ## 指令集 24 | YAME 支持以下指令的汇编、反汇编与模拟。 25 | 26 | 支持的 R 指令有: 27 | - add 28 | - addu 29 | - and 30 | - div 31 | - divu 32 | - jalr 33 | - jr 34 | - mfhi 35 | - mflo 36 | - mthi 37 | - mtlo 38 | - mult 39 | - multu 40 | - nor 41 | - or 42 | - sll 43 | - sllv 44 | - slt 45 | - sltu 46 | - sra 47 | - srav 48 | - srl 49 | - srlv 50 | - sub 51 | - subu 52 | - syscall(部分) 53 | - xor 54 | 55 | 支持的 I 指令有: 56 | - addi 57 | - addiu 58 | - andi 59 | - beq 60 | - bgez 61 | - bgezal 62 | - bgtz 63 | - blez 64 | - bltz 65 | - bltzal 66 | - bne 67 | - lb 68 | - lbu 69 | - lh 70 | - lhu 71 | - lui 72 | - lw 73 | - ori 74 | - sb 75 | - slti 76 | - sltiu 77 | - sh 78 | - sw 79 | - xori 80 | 81 | 支持的 J 指令有: 82 | - j 83 | - jal 84 | 85 | 支持的格式指令有: 86 | - .text 87 | - .data 88 | - .2byte 89 | - .4byte 90 | - .8byte 91 | - .ascii 92 | - .asciiz 93 | - .byte 94 | - .dword 95 | - .half 96 | - .space 97 | - .word 98 | 99 | 支持的 syscall 功能有: 100 | - 1(输出整数) 101 | - 4(输出字符串) 102 | - 5(输入整数) 103 | - 8(输入字符串) 104 | - 10(退出) 105 | - 11(输出字符) 106 | - 12(读入字符) 107 | - 30(读入系统时间) 108 | - 41(随机整数) 109 | - 42(有范围的随机整数) 110 | 111 | ## 伪指令 112 | YAME 最大的亮点是对自定义伪指令的支持。打开“设置 - 伪指令”窗口,即可添加、删除或修改伪指令。 113 | 114 | 用户提供伪指令名、操作数数量与对应的真指令后,YAME 会在汇编时将伪指令转换为对应真指令。 115 | 116 | 在“真指令”部分中,可以填入 `[x]` 表示第 x 个操作数。用 `@` 包围的标签名代表该标签的地址。例如,`@[2]@` 表示第 2 个操作数的地址(要求第 2 个操作数必须是标签)。 117 | 118 | YAME 的配置文件中,已经编写好了一些伪指令,可以打开伪指令窗口进行查看与修改。 119 | 120 | ## 模拟 121 | YAME 具有简单的模拟与调试功能。“模拟”界面是一个简单的模拟终端,可以通过 syscall 指令向终端输出信息,或从终端读入用户输入信息。终端输出信息用白色字表示,用户输入信息用绿色字表示。 122 | 123 | 可以使用 test 文件夹中的 guess.asm 文件(一个猜数字游戏,[原 github 地址点此](https://github.com/plazagonzalezd/MIPS-Game),guess.asm 文件在原文件基础上有修改)测试 YAME 的汇编与模拟功能。 124 | 125 | ## 依赖 126 | 如果想要从源代码运行 YAME,需要以下环境: 127 | - Python 3.5+ 128 | - PyQt5 129 | -------------------------------------------------------------------------------- /manual/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/manual/README.md -------------------------------------------------------------------------------- /manual/manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/manual/manual.pdf -------------------------------------------------------------------------------- /screenshot/assemble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/screenshot/assemble.png -------------------------------------------------------------------------------- /screenshot/pseudo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/screenshot/pseudo.png -------------------------------------------------------------------------------- /screenshot/simulate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/screenshot/simulate.png -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/assemble_exception.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Exception for assembling procedure 3 | # ------------------------------------------------------------------------------ 4 | class AssembleException(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/assembler.py: -------------------------------------------------------------------------------- 1 | import re 2 | import com.tsreaper.yame.assemble.instruction as instruction 3 | import com.tsreaper.yame.assemble.pseudo as pseudo 4 | import com.tsreaper.yame.assemble.data as data 5 | import com.tsreaper.yame.assemble.label as label 6 | import com.tsreaper.yame.assemble.immediate as immediate 7 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 8 | import com.tsreaper.yame.constant.config as config 9 | from com.tsreaper.yame.constant.instruction_template import * 10 | from com.tsreaper.yame.constant.token_regex import * 11 | 12 | # Memory 13 | mem = None 14 | 15 | # Text segment address 16 | text_addr = None 17 | 18 | # Data segment address 19 | data_addr = None 20 | 21 | # If current statement is text 22 | is_text = None 23 | 24 | # ------------------------------------------------------------------------------ 25 | # Split one instruction into tokens 26 | # ------------------------------------------------------------------------------ 27 | def split_tokens(text): 28 | lbl = None 29 | operation = None 30 | operand = None 31 | 32 | tokens = text.split() 33 | 34 | if len(tokens) > 0: 35 | if tokens[0][-1] == ':': 36 | # Label 37 | lbl = tokens[0][:-1] 38 | tokens.pop(0) 39 | text = text.replace(lbl+':', '', 1) 40 | 41 | if len(tokens) > 0: 42 | # Operation and operand 43 | operation = tokens[0] 44 | text = text.replace(operation, '', 1) 45 | operand = re.split(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''', text) if len(tokens) > 1 else [] 46 | 47 | lst = [] 48 | for o in operand: 49 | o = o.strip() 50 | if o != '' and o != ',': 51 | lst.append(o) 52 | operand = lst 53 | 54 | return (lbl, operation, operand) 55 | 56 | # ------------------------------------------------------------------------------ 57 | # Split whole assembly code into lines of tokens 58 | # ------------------------------------------------------------------------------ 59 | def split_lines(text): 60 | # Remove comments 61 | text = re.sub(COMMENT_REGEX, '', text) 62 | 63 | # Get valid lines 64 | splited = re.split('\n|\r|\n\r', text) 65 | lines = [] 66 | line_num = 0 67 | 68 | # Deal with each statement 69 | for line in splited: 70 | line_num += 1 71 | lines.append([]) 72 | 73 | for stm in line.split(';'): 74 | # Get tokens and parse pseudo instructions 75 | lbl, operation, operand = split_tokens(stm) 76 | 77 | try: 78 | parsed = pseudo.parse_pseudo(lbl, operation, operand, stm) 79 | except AssembleException as e: 80 | raise AssembleException('Line %d: ' % (line_num) + str(e)) 81 | 82 | for real in parsed.split(';'): 83 | # Parse real instructions 84 | lines[-1].append(split_tokens(real)) 85 | 86 | return lines 87 | 88 | # ------------------------------------------------------------------------------ 89 | # Check text address validity 90 | # ------------------------------------------------------------------------------ 91 | def check_text_addr(addr, line_num): 92 | if addr % 4 != 0: 93 | raise AssembleException('Line %d: Text segment address 0x%08x is not aligned to 4 bytes.' % (line_num, addr)) 94 | if addr < config.TEXT_ADDR_LOWER or addr > config.TEXT_ADDR_UPPER + 1: 95 | raise AssembleException('Line %d: Text segment address 0x%08x out of range.' % (line_num, addr)) 96 | 97 | # ------------------------------------------------------------------------------ 98 | # Check data address validity 99 | # ------------------------------------------------------------------------------ 100 | def check_data_addr(addr, line_num): 101 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER + 1: 102 | raise AssembleException('Line %d: Data segment address 0x%08x out of range.' % (line_num, addr)) 103 | 104 | # ------------------------------------------------------------------------------ 105 | # Parse data to binary machine code and record labels 106 | # ------------------------------------------------------------------------------ 107 | def process_label_and_data(lbl, operation, operand, line_num): 108 | global mem, text_addr, data_addr, is_text 109 | 110 | addr = text_addr if is_text else data_addr 111 | processed = True 112 | 113 | if operation != None: 114 | # Process operation 115 | if operation == '.text': 116 | # Text segment indicator 117 | if lbl != None: 118 | raise AssembleException('Line %d: Unexpected label.' % (line_num)) 119 | 120 | if len(operand) > 1: 121 | raise AssembleException('Line %d: Unexpected number of operand.' % (line_num)) 122 | elif len(operand) == 1: 123 | try: 124 | imm = immediate.value_of(operand[0]) 125 | except AssembleException as e: 126 | raise AssembleException('Line %d: %s' % (line_num, str(e))) 127 | text_addr = imm 128 | 129 | is_text = True 130 | check_text_addr(text_addr, line_num) 131 | 132 | return (text_addr, True) 133 | 134 | elif operation == '.data': 135 | # Data segment indicator 136 | if lbl != None: 137 | raise AssembleException('Line %d: Unexpected label.' % (line_num)) 138 | 139 | if len(operand) > 1: 140 | raise AssembleException('Line %d: Unexpected number of operand.' % (line_num)) 141 | elif len(operand) == 1: 142 | try: 143 | imm = immediate.value_of(operand[0]) 144 | except AssembleException as e: 145 | raise AssembleException('Line %d: %s' % (line_num, str(e))) 146 | data_addr = imm 147 | 148 | is_text = False 149 | check_data_addr(data_addr, line_num) 150 | 151 | return (data_addr, True) 152 | 153 | elif is_text: 154 | # Text statement 155 | check_text_addr(text_addr + 4, line_num) 156 | text_addr += 4 157 | processed = False 158 | 159 | else: 160 | # Data statement 161 | try: 162 | if operation.lower() not in DATA_INSTRUCTIONS.keys(): 163 | raise AssembleException('Unknown operation "%s" in data segment.' % (operation)) 164 | data_arr = data.get_binary(operation, operand) 165 | except AssembleException as e: 166 | raise AssembleException('Line %d: %s' % (line_num, str(e))) 167 | 168 | check_data_addr(data_addr + len(data_arr), line_num) 169 | for i in range(0, len(data_arr)): 170 | mem[data_addr + i] = data_arr[i] 171 | data_addr += len(data_arr) 172 | 173 | if lbl != None: 174 | # Process label 175 | try: 176 | label.insert(lbl, addr) 177 | except AssembleException as e: 178 | raise AssembleException('Line %d: %s' % (line_num, str(e))) 179 | 180 | return (addr, processed) 181 | 182 | # ------------------------------------------------------------------------------ 183 | # Parse text to binary machine code 184 | # ------------------------------------------------------------------------------ 185 | def process_text(operation, operand, addr, line_num): 186 | try: 187 | if operation in R_INSTRUCTIONS.keys(): 188 | bin_code = instruction.get_r_binary(operation, operand) 189 | elif operation in I_INSTRUCTIONS.keys(): 190 | bin_code = instruction.get_i_binary(operation, operand, addr) 191 | elif operation in J_INSTRUCTIONS.keys(): 192 | bin_code = instruction.get_j_binary(operation, operand) 193 | else: 194 | raise AssembleException('Unknown operation "%s" in text segment.' % (operation)) 195 | except AssembleException as e: 196 | raise AssembleException('Line %d: %s' % (line_num, str(e))) 197 | 198 | mem[addr] = bin_code >> 24 199 | mem[addr+1] = (bin_code >> 16) & 0xFF 200 | mem[addr+2] = (bin_code >> 8) & 0xFF 201 | mem[addr+3] = bin_code & 0XFF 202 | 203 | # ------------------------------------------------------------------------------ 204 | # Assemble MIPS code to binary machine code 205 | # ------------------------------------------------------------------------------ 206 | def assemble(text): 207 | global mem, text_addr, data_addr, is_text 208 | 209 | # Init 210 | mem = bytearray(config.MEM_SIZE) 211 | text_addr = config.TEXT_ADDR_LOWER 212 | data_addr = config.DATA_ADDR_LOWER 213 | is_text = True 214 | label.clear() 215 | 216 | # Get lines 217 | lines = split_lines(text) 218 | 219 | # Process labels and data 220 | texts = [] 221 | for i in range(0, len(lines)): 222 | for s in lines[i]: 223 | addr, processed = process_label_and_data(*s, i+1) 224 | if not processed: 225 | texts.append((*s[1:], addr, i+1)) 226 | 227 | # Process texts 228 | for s in texts: 229 | process_text(*s) 230 | 231 | try: 232 | return mem, label.value_of('main') 233 | except: 234 | return mem, -1 235 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/data.py: -------------------------------------------------------------------------------- 1 | import com.tsreaper.yame.assemble.immediate as immediate 2 | import com.tsreaper.yame.assemble.string as string 3 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 4 | from com.tsreaper.yame.constant.instruction_template import * 5 | import com.tsreaper.yame.constant.config as config 6 | 7 | def get_binary(operation, operand): 8 | bin_code = bytearray() 9 | 10 | if DATA_INSTRUCTIONS[operation][0] == 'imm': 11 | # Data about immediates 12 | b = DATA_INSTRUCTIONS[operation][1] 13 | 14 | for o in operand: 15 | if operation != '.space': 16 | imm = immediate.value_of(o, b*8) 17 | for i in range(0, b): 18 | bin_code.append((imm>>((b-1-i)*8)) & 0xFF) 19 | else: 20 | imm = immediate.value_of(o) 21 | if imm > config.MEM_SIZE: 22 | raise AssembleException('.space consumes too much space.') 23 | for i in range(0, imm): 24 | bin_code.append(0) 25 | else: 26 | # Data about strings 27 | for o in operand: 28 | s = string.value_of(o) 29 | for c in s: 30 | bin_code.append(ord(c)) 31 | if operation == '.asciiz': 32 | bin_code.append(0) 33 | 34 | return bin_code 35 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/immediate.py: -------------------------------------------------------------------------------- 1 | import re 2 | import com.tsreaper.yame.assemble.label as label 3 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Find the value of an immediate 7 | # ------------------------------------------------------------------------------ 8 | def value_of(imm, b = 32): 9 | if isinstance(imm, str): 10 | # Deal with character 11 | while True: 12 | regex_res = re.search('\'(.+?)\'', imm) 13 | if regex_res == None: 14 | break 15 | 16 | c = eval('"' + regex_res.group(1) + '"') 17 | imm = re.sub('\'(.+?)\'', str(ord(c)), imm, 1) 18 | 19 | # Deal with label 20 | while True: 21 | regex_res = re.search('@(.+?)@', imm) 22 | if regex_res == None: 23 | break 24 | 25 | addr = label.value_of(regex_res.group(1)) 26 | imm = re.sub('@(.+?)@', hex(addr), imm, 1) 27 | 28 | try: 29 | res = eval(imm.replace('/', '//')) 30 | except: 31 | raise AssembleException('Invalid immediate "%s".' % (imm)) 32 | else: 33 | res = imm 34 | 35 | if not isinstance(res, int): 36 | raise AssembleException('Invalid immediate "%s".' % (imm)) 37 | 38 | return res & (2**b - 1) 39 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/immediate_register.py: -------------------------------------------------------------------------------- 1 | import re 2 | import com.tsreaper.yame.assemble.register as register 3 | import com.tsreaper.yame.assemble.immediate as immediate 4 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 5 | from com.tsreaper.yame.constant.token_regex import * 6 | 7 | # ------------------------------------------------------------------------------ 8 | # Find the value of an immediate(register) 9 | # ------------------------------------------------------------------------------ 10 | def value_of(imm_rs, b): 11 | try: 12 | imm, rs = re.search(IMMEDIATE_REGISTER_REGEX, imm_rs).groups() 13 | except: 14 | raise AssembleException('Invalid immediate(rs) "%s".' % (imm_rs)) 15 | 16 | return (immediate.value_of(imm, b), register.value_of(rs)) 17 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/instruction.py: -------------------------------------------------------------------------------- 1 | import com.tsreaper.yame.assemble.register as register 2 | import com.tsreaper.yame.assemble.immediate as immediate 3 | import com.tsreaper.yame.assemble.immediate_register as immediate_register 4 | import com.tsreaper.yame.assemble.label as label 5 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 6 | from com.tsreaper.yame.constant.instruction_template import * 7 | 8 | # ------------------------------------------------------------------------------ 9 | # Get binary machine code of an R-type instruction 10 | # ------------------------------------------------------------------------------ 11 | def get_r_binary(operation, operand): 12 | t = R_INSTRUCTIONS[operation] 13 | 14 | if len(operand) != len(t[0]): 15 | # Unexpected number of operand 16 | raise AssembleException('Unexpected number of operand.') 17 | 18 | # Read values from operand 19 | rs, rt, rd, sa, func = (0, 0, 0, 0, t[1]) 20 | for i in range(0, len(t[0])): 21 | if t[0][i] == 'rd': 22 | rd = register.value_of(operand[i]) 23 | elif t[0][i] == 'rs': 24 | rs = register.value_of(operand[i]) 25 | elif t[0][i] == 'rt': 26 | rt = register.value_of(operand[i]) 27 | elif t[0][i] == 'sa': 28 | sa = immediate.value_of(operand[i], 5) 29 | 30 | return (rs<<21) | (rt<<16) | (rd<<11) | (sa<<6) | func 31 | 32 | # ------------------------------------------------------------------------------ 33 | # Get binary machine code of an I-type instruction 34 | # ------------------------------------------------------------------------------ 35 | def get_i_binary(operation, operand, addr): 36 | t = I_INSTRUCTIONS[operation] 37 | 38 | if len(operand) != len(t[0]): 39 | # Unexpected number of operand 40 | raise AssembleException('Unexpected number of operand.') 41 | 42 | # Read values from operand 43 | op, rs, rt, imm = (t[1], 0, 0, 0) 44 | 45 | if operation == 'bgez': 46 | rt = 1 47 | elif operation == 'bgezal': 48 | rt = 17 49 | elif operation == 'bltz': 50 | rt = 0 51 | elif operation == 'bltzal': 52 | rt = 16 53 | 54 | for i in range(0, len(t[0])): 55 | if t[0][i] == 'rs': 56 | rs = register.value_of(operand[i]) 57 | elif t[0][i] == 'rt': 58 | rt = register.value_of(operand[i]) 59 | elif t[0][i] == 'imm': 60 | imm = immediate.value_of(operand[i], 16) 61 | elif t[0][i] == 'imm(rs)': 62 | imm, rs = immediate_register.value_of(operand[i], 16) 63 | elif t[0][i] == 'label': 64 | lbl_addr = label.value_of(operand[i]) 65 | try: 66 | imm = immediate.value_of((lbl_addr - addr)//4 - 1, 16) 67 | except: 68 | # Label too far 69 | raise AssembleException('Can\'t reach label "%s".' % (operand[i])) 70 | 71 | return (op<<26) | (rs<<21) | (rt<<16) | imm 72 | 73 | # ------------------------------------------------------------------------------ 74 | # Get binary machine code of an J-type instruction 75 | # ------------------------------------------------------------------------------ 76 | def get_j_binary(operation, operand): 77 | t = J_INSTRUCTIONS[operation] 78 | 79 | if len(operand) != len(t[0]): 80 | # Unexpected number of operand 81 | raise AssembleException('Unexpected number of operand.') 82 | 83 | # Read values from operand 84 | op, imm = (t[1], 0) 85 | for i in range(0, len(t[0])): 86 | if t[0][i] == 'label': 87 | lbl_addr = label.value_of(operand[i]) 88 | try: 89 | imm = immediate.value_of(lbl_addr//4, 26) 90 | except: 91 | # Label too far 92 | raise AssembleException('Can\'t reach label "%s".' % (operand[i])) 93 | 94 | return (op<<26) | imm 95 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/label.py: -------------------------------------------------------------------------------- 1 | import re 2 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 3 | from com.tsreaper.yame.constant.token_regex import * 4 | 5 | # Dictionary. Stores labels and their address 6 | label_dict = {} 7 | 8 | # ------------------------------------------------------------------------------ 9 | # Insert a new label 10 | # ------------------------------------------------------------------------------ 11 | def insert(label, addr): 12 | if re.search(LABEL_REGEX, label) == None: 13 | # Invalid format 14 | raise AssembleException('Invalid label "%s"' % (label)) 15 | if label in label_dict.keys(): 16 | # Duplicate label 17 | raise AssembleException('Duplicate "%s"' % (label)) 18 | 19 | label_dict[label] = addr 20 | 21 | # ------------------------------------------------------------------------------ 22 | # Find the address of a label 23 | # ------------------------------------------------------------------------------ 24 | def value_of(label): 25 | if re.search(LABEL_REGEX, label) == None: 26 | # Invalid format 27 | raise AssembleException('Invalid label "%s"' % (label)) 28 | if label not in label_dict.keys(): 29 | # Label not found 30 | raise AssembleException('Can\'t find label "%s"' % (label)) 31 | 32 | return label_dict[label] 33 | 34 | # ------------------------------------------------------------------------------ 35 | # Clear all the stored labels 36 | # ------------------------------------------------------------------------------ 37 | def clear(): 38 | label_dict.clear() 39 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/pseudo.py: -------------------------------------------------------------------------------- 1 | import re 2 | import configparser 3 | 4 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 5 | 6 | # Dictionary. Stores pseudo instructions 7 | pseudo_dict = {} 8 | 9 | # ------------------------------------------------------------------------------ 10 | # Load pseudo instructions from config file 11 | # ------------------------------------------------------------------------------ 12 | def load_pseudo(filename): 13 | conf = configparser.ConfigParser() 14 | conf.read(filename) 15 | 16 | for sec in conf.sections(): 17 | if sec == '`': 18 | # Skip assembler config 19 | continue 20 | 21 | pseudo_dict[sec] = (conf.getint(sec, 'operand'), conf.get(sec, 'real')) 22 | 23 | # ------------------------------------------------------------------------------ 24 | # Save pseudo instructions to config file 25 | # ------------------------------------------------------------------------------ 26 | def save_pseudo(filename): 27 | conf = configparser.ConfigParser() 28 | conf.read(filename) 29 | conf.clear() 30 | 31 | for op in pseudo_dict.keys(): 32 | conf.add_section(op) 33 | conf.set(op, 'operand', str(pseudo_dict[op][0])) 34 | conf.set(op, 'real', pseudo_dict[op][1]) 35 | 36 | conf.write(open(filename, 'w')) 37 | 38 | # ------------------------------------------------------------------------------ 39 | # Parse pseudo instructions to real instructions 40 | # ------------------------------------------------------------------------------ 41 | def parse_pseudo(lbl, operation, operand, stm): 42 | if operation not in pseudo_dict.keys(): 43 | return stm 44 | 45 | num = pseudo_dict[operation][0] 46 | if num != len(operand): 47 | raise AssembleException('Unexpected number of operand.') 48 | 49 | # Replacing 50 | statement = pseudo_dict[operation][1] 51 | for i in range(0, num): 52 | statement = statement.replace('[%d]' % (i+1), operand[i]) 53 | 54 | if lbl != None: 55 | statement = lbl + ': ' + statement 56 | 57 | return statement 58 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/register.py: -------------------------------------------------------------------------------- 1 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 2 | from com.tsreaper.yame.constant.register_list import * 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Find the id of a register 6 | # ------------------------------------------------------------------------------ 7 | def value_of(reg): 8 | reg = reg.lower() 9 | 10 | if reg in REGISTER_LIST: 11 | return REGISTER_LIST.index(reg) 12 | if reg in REGISTER_NUM_LIST: 13 | return REGISTER_NUM_LIST.index(reg) 14 | raise AssembleException('Invalid register "%s".' % (reg)) 15 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/assemble/string.py: -------------------------------------------------------------------------------- 1 | from com.tsreaper.yame.assemble.assemble_exception import AssembleException 2 | 3 | # ------------------------------------------------------------------------------ 4 | # Find the value of a string 5 | # ------------------------------------------------------------------------------ 6 | def value_of(s): 7 | res = eval(s) 8 | if not isinstance(res, str): 9 | raise AssembleException('Invalid string "%s".' % (s)) 10 | return res 11 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/constant/config.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | 3 | # Config file name 4 | CONFIG_FILE = 'config.cfg' 5 | 6 | # Memory size 7 | MEM_SIZE = 256 8 | MAX_MEM_SIZE = 1048576 9 | 10 | # Text segment address range 11 | TEXT_ADDR_LOWER = 0x00 12 | TEXT_ADDR_UPPER = 0x7F 13 | 14 | # Data segment address range 15 | DATA_ADDR_LOWER = 0x80 16 | DATA_ADDR_UPPER = 0XFF 17 | 18 | # ------------------------------------------------------------------------------ 19 | # Load config from file 20 | # ------------------------------------------------------------------------------ 21 | def load_config(filename): 22 | global MEM_SIZE, TEXT_ADDR_LOWER, TEXT_ADDR_UPPER, DATA_ADDR_LOWER, DATA_ADDR_UPPER 23 | 24 | conf = configparser.ConfigParser() 25 | conf.read(filename) 26 | 27 | MEM_SIZE = conf.getint('`', 'mem_size') 28 | TEXT_ADDR_LOWER = conf.getint('`', 'text_addr_lower') 29 | TEXT_ADDR_UPPER = conf.getint('`', 'text_addr_upper') 30 | DATA_ADDR_LOWER = conf.getint('`', 'data_addr_lower') 31 | DATA_ADDR_UPPER = conf.getint('`', 'data_addr_upper') 32 | 33 | # ------------------------------------------------------------------------------ 34 | # Save config to file 35 | # ------------------------------------------------------------------------------ 36 | def save_config(filename): 37 | global MEM_SIZE, TEXT_ADDR_LOWER, TEXT_ADDR_UPPER, DATA_ADDR_LOWER, DATA_ADDR_UPPER 38 | 39 | conf = configparser.ConfigParser() 40 | conf.read(filename) 41 | 42 | conf.add_section('`') 43 | conf.set('`', 'mem_size', str(MEM_SIZE)) 44 | conf.set('`', 'text_addr_lower', str(TEXT_ADDR_LOWER)) 45 | conf.set('`', 'text_addr_upper', str(TEXT_ADDR_UPPER)) 46 | conf.set('`', 'data_addr_lower', str(DATA_ADDR_LOWER)) 47 | conf.set('`', 'data_addr_upper', str(DATA_ADDR_UPPER)) 48 | 49 | conf.write(open(filename, 'w')) 50 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/constant/instruction_template.py: -------------------------------------------------------------------------------- 1 | R_INSTRUCTIONS = { 2 | 'add': [['rd', 'rs', 'rt'], 0b100000], 3 | 'addu': [['rd', 'rs', 'rt'], 0b100001], 4 | 'and': [['rd', 'rs', 'rt'], 0b100100], 5 | 'break': [[], 0b001101], 6 | 'div': [['rs', 'rt'], 0b011010], 7 | 'divu': [['rs', 'rt'], 0b011011], 8 | 'jalr': [['rd', 'rs'], 0b001001], 9 | 'jr': [['rs'], 0b001000], 10 | 'mfhi': [['rd'], 0b010000], 11 | 'mflo': [['rd'], 0b010010], 12 | 'mthi': [['rs'], 0b010001], 13 | 'mtlo': [['rs'], 0b010011], 14 | 'mult': [['rs', 'rt'], 0b011000], 15 | 'multu': [['rs', 'rt'], 0b011001], 16 | 'nor': [['rd', 'rs', 'rt'], 0b100111], 17 | 'or': [['rd', 'rs', 'rt'], 0b100101], 18 | 'sll': [['rd', 'rt', 'sa'], 0b000000], 19 | 'sllv': [['rd', 'rt', 'rs'], 0b000100], 20 | 'slt': [['rd', 'rs', 'rt'], 0b101010], 21 | 'sltu': [['rd', 'rs', 'rt'], 0b101011], 22 | 'sra': [['rd', 'rt', 'sa'], 0b000011], 23 | 'srav': [['rd', 'rt', 'rs'], 0b000111], 24 | 'srl': [['rd', 'rt', 'sa'], 0b000010], 25 | 'srlv': [['rd', 'rt', 'rs'], 0b000110], 26 | 'sub': [['rd', 'rs', 'rt'], 0b100010], 27 | 'subu': [['rd', 'rs', 'rt'], 0b100011], 28 | 'syscall': [[], 0b001100], 29 | 'xor': [['rd', 'rs', 'rt'], 0b100110], 30 | } 31 | 32 | I_INSTRUCTIONS = { 33 | 'addi': [['rt', 'rs', 'imm'], 0b001000], 34 | 'addiu': [['rt', 'rs', 'imm'], 0b001001], 35 | 'andi': [['rt', 'rs', 'imm'], 0b001100], 36 | 'beq': [['rs', 'rt', 'label'], 0b000100], 37 | 'bgez': [['rs', 'label'], 0b000001], 38 | 'bgezal': [['rs', 'label'], 0b000001], 39 | 'bgtz': [['rs', 'label'], 0b000111], 40 | 'blez': [['rs', 'label'], 0b000110], 41 | 'bltz': [['rs', 'label'], 0b000001], 42 | 'bltzal': [['rs', 'label'], 0b000001], 43 | 'bne': [['rs', 'rt', 'label'], 0b000101], 44 | 'lb': [['rt', 'imm(rs)'], 0b100000], 45 | 'lbu': [['rt', 'imm(rs)'], 0b100100], 46 | 'lh': [['rt', 'imm(rs)'], 0b100001], 47 | 'lhu': [['rt', 'imm(rs)'], 0b100101], 48 | 'lui': [['rt', 'imm'], 0b001111], 49 | 'lw': [['rt', 'imm(rs)'], 0b100011], 50 | 'lwc1': [['rt', 'imm(rs)'], 0b110001], 51 | 'ori': [['rt', 'rs', 'imm'], 0b001101], 52 | 'sb': [['rt', 'imm(rs)'], 0b101000], 53 | 'slti': [['rt', 'rs', 'imm'], 0b001010], 54 | 'sltiu': [['rt', 'rs', 'imm'], 0b001011], 55 | 'sh': [['rt', 'imm(rs)'], 0b101001], 56 | 'sw': [['rt', 'imm(rs)'], 0b101011], 57 | 'swc1': [['rt', 'imm(rs)'], 0b111001], 58 | 'xori': [['rt', 'rs', 'imm'], 0b001110], 59 | } 60 | 61 | J_INSTRUCTIONS = { 62 | 'j': [['label'], 0b000010], 63 | 'jal': [['label'], 0b000011], 64 | } 65 | 66 | DATA_INSTRUCTIONS = { 67 | '.2byte': ['imm', 2], 68 | '.4byte': ['imm', 4], 69 | '.8byte': ['imm', 8], 70 | '.ascii': ['str', 1], 71 | '.asciiz': ['str', 1], 72 | '.byte': ['imm', 1], 73 | '.dword': ['imm', 8], 74 | '.half': ['imm', 2], 75 | '.space': ['imm', 1], 76 | '.word': ['imm', 4], 77 | } 78 | 79 | # Construct "func/op code - operation name" dictionary 80 | R_FUNCCODES = {} 81 | for o in R_INSTRUCTIONS.keys(): 82 | R_FUNCCODES[R_INSTRUCTIONS[o][1]] = o 83 | 84 | I_OPCODES = {} 85 | for o in I_INSTRUCTIONS.keys(): 86 | I_OPCODES[I_INSTRUCTIONS[o][1]] = o 87 | 88 | J_OPCODES = {} 89 | for o in J_INSTRUCTIONS.keys(): 90 | J_OPCODES[J_INSTRUCTIONS[o][1]] = o 91 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/constant/register_list.py: -------------------------------------------------------------------------------- 1 | REGISTER_LIST = ( 2 | '$zero', '$at', '$v0', '$v1', '$a0', '$a1', '$a2', 3 | '$a3', '$t0', '$t1', '$t2', '$t3', '$t4', '$t5', 4 | '$t6', '$t7', '$s0', '$s1', '$s2', '$s3', '$s4', 5 | '$s5', '$s6', '$s7', '$t8', '$t9', '$k0', '$k1', 6 | '$gp', '$sp', '$fp', '$ra' 7 | ) 8 | 9 | REGISTER_NUM_LIST = ( 10 | '$0', '$1', '$2', '$3', '$4', '$5', '$6', '$7', 11 | '$8', '$9', '$10', '$11', '$12', '$13', '$14', '$15', 12 | '$16', '$17', '$18', '$19', '$20', '$21', '$22', '$23', 13 | '$24', '$25', '$26', '$27', '$28', '$29', '$30', '$31', 14 | ) 15 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/constant/token_regex.py: -------------------------------------------------------------------------------- 1 | LABEL_REGEX = '^[a-zA-Z_\$\.][0-9a-zA-Z_\$\.]*$' 2 | COMMENT_REGEX = '(?:#.*)|(?://.*)|(?:/\*.*?\*/)' 3 | IMMEDIATE_REGISTER_REGEX = '^(.*)\((.*?)\)$' 4 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/disassemble/data.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Disassemble data 3 | # ------------------------------------------------------------------------------ 4 | def disassemble_data(code, stm_num): 5 | return 'L%08X: .word 0x%08x' % (stm_num, code) 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/disassemble/disassemble_exception.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Exception for disassembling procedure 3 | # ------------------------------------------------------------------------------ 4 | class DisassembleException(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/disassemble/disassembler.py: -------------------------------------------------------------------------------- 1 | import com.tsreaper.yame.disassemble.instruction as instruction 2 | import com.tsreaper.yame.disassemble.data as data 3 | from com.tsreaper.yame.disassemble.disassemble_exception import DisassembleException 4 | import com.tsreaper.yame.constant.config as config 5 | 6 | # ------------------------------------------------------------------------------ 7 | # Disassemble binary machine code to MIPS code 8 | # ------------------------------------------------------------------------------ 9 | def disassemble(mem): 10 | res = '' 11 | 12 | zero = True 13 | for i in range(config.TEXT_ADDR_LOWER, config.TEXT_ADDR_UPPER+1, 4): 14 | try: 15 | code = (int(mem[i])<<24) | (int(mem[i+1])<<16) | (int(mem[i+2])<<8) | int(mem[i+3]) 16 | except: 17 | raise DisassembleException('Invalid binary file.') 18 | 19 | if code != 0: 20 | if zero: 21 | res += '.text 0x%08x\n' % (i) 22 | res += instruction.disassemble_instruction(code, i//4) + '\n' 23 | zero = False 24 | else: 25 | zero = True 26 | 27 | zero = True 28 | for i in range(config.DATA_ADDR_LOWER, config.DATA_ADDR_UPPER+1, 4): 29 | try: 30 | code = (int(mem[i])<<24) | (int(mem[i+1])<<16) | (int(mem[i+2])<<8) | int(mem[i+3]) 31 | except: 32 | raise DisassembleException('Invalid binary file.') 33 | 34 | if code != 0: 35 | if zero: 36 | res += '.data 0x%08x\n' % (i) 37 | res += data.disassemble_data(code, i//4) + '\n' 38 | zero = False 39 | else: 40 | zero = True 41 | 42 | return res 43 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/disassemble/instruction.py: -------------------------------------------------------------------------------- 1 | import com.tsreaper.yame.disassemble.register as register 2 | from com.tsreaper.yame.disassemble.disassemble_exception import DisassembleException 3 | from com.tsreaper.yame.constant.instruction_template import * 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Get a signed integer 7 | # ------------------------------------------------------------------------------ 8 | def get_signed(x, b = 16): 9 | if (x>>(b-1)) == 1: 10 | return x - (2**b) 11 | else: 12 | return x 13 | 14 | # ------------------------------------------------------------------------------ 15 | # Disassemble an R-type instruction 16 | # ------------------------------------------------------------------------------ 17 | def disassemble_r_instruction(code): 18 | rs = (code>>21) & (2**5-1) 19 | rt = (code>>16) & (2**5-1) 20 | rd = (code>>11) & (2**5-1) 21 | sa = (code>>6) & (2**5-1) 22 | func = code & (2**6-1) 23 | 24 | if func not in R_FUNCCODES.keys(): 25 | raise DisassembleException('Invalid function code of R-type instruction.') 26 | 27 | # Get operation name and operand 28 | operation = R_FUNCCODES[func] 29 | operand = R_INSTRUCTIONS[operation][0] 30 | res = operation 31 | 32 | # Get operand values 33 | for i in range(0, len(operand)): 34 | if i > 0: 35 | res += ',' 36 | if operand[i] == 'rd': 37 | res += ' ' + register.name_of(rd) 38 | elif operand[i] == 'rs': 39 | res += ' ' + register.name_of(rs) 40 | elif operand[i] == 'rt': 41 | res += ' ' + register.name_of(rt) 42 | elif operand[i] == 'sa': 43 | res += ' %d' % (sa) 44 | 45 | return res 46 | 47 | # ------------------------------------------------------------------------------ 48 | # Disassemble an I-type instruction 49 | # ------------------------------------------------------------------------------ 50 | def disassemble_i_instruction(code, stm_num): 51 | op = (code>>26) 52 | rs = (code>>21) & (2**5-1) 53 | rt = (code>>16) & (2**5-1) 54 | imm = code & (2**16-1) 55 | 56 | # Get operation name and operand 57 | if op == 0b00001: 58 | if rt == 1: 59 | operation = 'bgez' 60 | elif rt == 17: 61 | operation = 'bgezal' 62 | elif rt == 0: 63 | operation = 'bltz' 64 | elif rt == 16: 65 | operation = 'bltzal' 66 | else: 67 | operation = I_OPCODES[op] 68 | 69 | operand = I_INSTRUCTIONS[operation][0] 70 | res = operation 71 | 72 | # Get operand values 73 | for i in range(0, len(operand)): 74 | if i > 0: 75 | res += ',' 76 | if operand[i] == 'rs': 77 | res += ' ' + register.name_of(rs) 78 | elif operand[i] == 'rt': 79 | res += ' ' + register.name_of(rt) 80 | elif operand[i] == 'imm': 81 | res += ' 0x%04x' % (imm) 82 | elif operand[i] == 'imm(rs)': 83 | res += ' %d(%s)' % (get_signed(imm), register.name_of(rs)) 84 | elif operand[i] == 'label': 85 | res += ' L%08X' % (get_signed(imm) + stm_num + 1) 86 | 87 | return res 88 | 89 | # ------------------------------------------------------------------------------ 90 | # Disassemble a J-type instruction 91 | # ------------------------------------------------------------------------------ 92 | def disassemble_j_instruction(code): 93 | op = (code>>26) 94 | imm = code & (2**26-1) 95 | 96 | # Get operation name and operand 97 | operation = J_OPCODES[op] 98 | operand = J_INSTRUCTIONS[operation][0] 99 | res = operation 100 | 101 | # Get operand values 102 | for i in range(0, len(operand)): 103 | if i > 0: 104 | res += ',' 105 | if operand[i] == 'label': 106 | res += ' L%08X' % (imm) 107 | 108 | return res 109 | 110 | # ------------------------------------------------------------------------------ 111 | # Disassemble an instruction 112 | # ------------------------------------------------------------------------------ 113 | def disassemble_instruction(code, stm_num): 114 | op = (code>>26) 115 | if op == 0b000000: 116 | # R-type instruction 117 | res = disassemble_r_instruction(code) 118 | elif op in I_OPCODES.keys(): 119 | # I-type instruction 120 | res = disassemble_i_instruction(code, stm_num) 121 | elif op in J_OPCODES.keys(): 122 | # J-type instruction 123 | res = disassemble_j_instruction(code) 124 | else: 125 | raise DisassembleException('Invalid operation code.') 126 | 127 | return ('L%08X: ' % (stm_num)) + res 128 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/disassemble/register.py: -------------------------------------------------------------------------------- 1 | from com.tsreaper.yame.disassemble.disassemble_exception import DisassembleException 2 | from com.tsreaper.yame.constant.register_list import * 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Get name of a register 6 | # ------------------------------------------------------------------------------ 7 | def name_of(idx): 8 | if idx < 0 or idx >= len(REGISTER_LIST): 9 | raise DisassembleException('Invalid register index.') 10 | 11 | return REGISTER_LIST[idx] 12 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/io/bin_file.py: -------------------------------------------------------------------------------- 1 | from com.tsreaper.yame.io.file_exception import FileException 2 | import com.tsreaper.yame.constant.config as config 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Read BIN file 6 | # ------------------------------------------------------------------------------ 7 | def read(filename): 8 | file = open(filename, 'rb') 9 | mem = bytearray(file.read()) 10 | file.close() 11 | if len(mem) > config.MEM_SIZE: 12 | raise FileException('BIN file too large.') 13 | return mem 14 | 15 | # ------------------------------------------------------------------------------ 16 | # Write BIN file 17 | # ------------------------------------------------------------------------------ 18 | def write(filename, mem): 19 | file = open(filename, 'wb') 20 | file.write(mem) 21 | file.close() 22 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/io/coe_file.py: -------------------------------------------------------------------------------- 1 | import re 2 | from com.tsreaper.yame.io.file_exception import FileException 3 | import com.tsreaper.yame.constant.config as config 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Read COE file 7 | # ------------------------------------------------------------------------------ 8 | def read(filename): 9 | file = open(filename, 'r') 10 | content = file.read() 11 | file.close() 12 | 13 | # Read radix 14 | radix_res = re.search(r'memory_initialization_radix\s*=\s*(\d+?);', content) 15 | if radix_res == None: 16 | raise FileException('Could not read radix info.') 17 | radix = int(radix_res.group(1)) 18 | if radix != 2 and radix != 10 and radix != 16: 19 | raise FileException('Invalid radix %d.' % (radix)) 20 | 21 | # Read data 22 | data_res = re.search(r'memory_initialization_vector=([\s\S]*?);', content) 23 | if data_res == None: 24 | raise FileException('Could not read data.') 25 | data = data_res.group(1).split(',') 26 | 27 | # Parse data 28 | mem = bytearray(config.MEM_SIZE) 29 | addr = 0 30 | 31 | for v in data: 32 | v = v.split()[0] 33 | if radix == 2: 34 | v = '0b' + v 35 | elif radix == 16: 36 | v = '0x' + v 37 | v = eval(v) 38 | 39 | try: 40 | mem[addr] = (v>>24) & 0xFF 41 | mem[addr+1] = (v>>16) & 0xFF 42 | mem[addr+2] = (v>>8) & 0xFF 43 | mem[addr+3] = v & 0xFF 44 | addr += 4 45 | except Exception as e: 46 | raise FileException('COE file too large.') 47 | 48 | return mem 49 | 50 | # ------------------------------------------------------------------------------ 51 | # Write coe file 52 | # ------------------------------------------------------------------------------ 53 | def write(filename, mem): 54 | file = open(filename, 'w') 55 | 56 | content = 'memory_initialization_radix=16;\n' 57 | content += 'memory_initialization_vector=' 58 | for i in range(0, len(mem), 4): 59 | v = (int(mem[i])<<24) | (int(mem[i+1])<<16) | (int(mem[i+2])<<8) | int(mem[i+3]) 60 | if i > 0: 61 | content += ', ' 62 | content += '%08X' % (v) 63 | content += ';' 64 | 65 | file.write(content) 66 | file.close() 67 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/io/file_exception.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Exception for file i/o 3 | # ------------------------------------------------------------------------------ 4 | class FileException(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/simulate/simulation_exception.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Exception for simulation procedure 3 | # ------------------------------------------------------------------------------ 4 | class SimulationException(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/simulate/simulation_info_exception.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Pause info for simulation procedure 3 | # ------------------------------------------------------------------------------ 4 | class SimulationInfoException(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/simulate/simulator.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | from com.tsreaper.yame.simulate.simulation_exception import SimulationException 4 | from com.tsreaper.yame.simulate.simulation_info_exception import SimulationInfoException 5 | from com.tsreaper.yame.constant.register_list import * 6 | import com.tsreaper.yame.constant.config as config 7 | 8 | # Maximum number of instruction executed 9 | STEP_LIMIT = 1000000 10 | 11 | pc = 0 # Program counter 12 | init_pc = 0 # Initial address 13 | 14 | mem = bytearray() # Memory 15 | init_mem = bytearray() # Initial memory 16 | 17 | reg = [0] * len(REGISTER_LIST) # Registers 18 | hi, lo = (0, 0) # Two special registers 19 | 20 | input_data = [] # Input data for syscall 21 | output_data = '' # Output data for syscall 22 | 23 | program_end = False # If simulation is finished 24 | 25 | # ------------------------------------------------------------------------------ 26 | # Get a signed integer 27 | # ------------------------------------------------------------------------------ 28 | def get_signed(x, b = 32): 29 | if (x>>(b-1)) == 1: 30 | return x - (2**b) 31 | else: 32 | return x 33 | 34 | # ------------------------------------------------------------------------------ 35 | # Read a null ended string from memory 36 | # ------------------------------------------------------------------------------ 37 | def read_string(addr): 38 | global pc 39 | 40 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 41 | raise SimulationException('PC = %d: Trying to read string out of data segment.' % (pc)) 42 | 43 | ret = '' 44 | while mem[addr] != 0: 45 | ret += chr(mem[addr]) 46 | addr += 1 47 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 48 | raise SimulationException('PC = %d: Trying to read string out of data segment.' % (pc)) 49 | 50 | return ret 51 | 52 | # ------------------------------------------------------------------------------ 53 | # Write a null ended string to memory 54 | # ------------------------------------------------------------------------------ 55 | def write_string(s, addr, sz): 56 | global pc 57 | 58 | sz = min(sz-1, len(s)) 59 | for i in range(0, sz): 60 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 61 | raise SimulationException('PC = %d: Trying to write string out of data segment.' % (pc)) 62 | mem[addr] = ord(s[i]) 63 | addr += 1 64 | 65 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 66 | raise SimulationException('PC = %d: Trying to write string out of data segment.' % (pc)) 67 | mem[addr] = 0 68 | 69 | # ------------------------------------------------------------------------------ 70 | # Reset the simulator 71 | # ------------------------------------------------------------------------------ 72 | def reset(): 73 | global pc, init_pc, mem, init_mem, reg, hi, lo, input_data, output_data, program_end 74 | 75 | # Reset PC 76 | pc = init_pc 77 | 78 | # Reset memory 79 | mem = bytearray(init_mem) 80 | 81 | # Reset registers 82 | for i in range(0, len(REGISTER_LIST)): 83 | reg[i] = 0 84 | reg[29] = config.DATA_ADDR_UPPER + 1 85 | hi, lo = (0, 0) 86 | 87 | input_data = [] 88 | output_data = '' 89 | 90 | program_end = False 91 | 92 | # ------------------------------------------------------------------------------ 93 | # Load machine code for simulation 94 | # ------------------------------------------------------------------------------ 95 | def load(_mem, _addr): 96 | global init_mem, init_pc 97 | 98 | init_mem = bytearray(_mem) 99 | init_pc = _addr 100 | reset() 101 | 102 | # ------------------------------------------------------------------------------ 103 | # Run one step of the program 104 | # ------------------------------------------------------------------------------ 105 | def step(): 106 | global pc, mem, reg, hi, lo, input_data, output_data, program_end 107 | 108 | # Get each part of instruction 109 | ins = (int(mem[pc])<<24) | (int(mem[pc+1])<<16) | (int(mem[pc+2])<<8) | int(mem[pc+3]) 110 | op = ins>>26 111 | r1 = (ins>>21) & 0x1F 112 | r2 = (ins>>16) & 0x1F 113 | r3 = (ins>>11) & 0x1F 114 | sa = (ins>>6) & 0x1F 115 | func = ins & 0x3F 116 | imm = ins & 0xFFFF 117 | lbl = ins & 0x3FFFFFF 118 | 119 | if pc < config.TEXT_ADDR_LOWER or pc > config.TEXT_ADDR_UPPER: 120 | program_end = True 121 | raise SimulationException('PC = %d: Program counter moved out of text segment.' % (pc)) 122 | 123 | if op == 0x00: 124 | # R-type instruction 125 | if func == 0x20: 126 | # Add 127 | reg[r3] = (reg[r1] + reg[r2]) & 0xFFFFFFFF 128 | if (reg[r1]>>31) == (reg[r2]>>31) and (reg[r1]>>31) != (reg[r3]>>31): 129 | program_end = True 130 | raise SimulationException('PC = %d: An overflow occurs when performing the add operation.' % (pc)) 131 | 132 | elif func == 0x21: 133 | # Addu 134 | reg[r3] = (reg[r1] + reg[r2]) & 0xFFFFFFFF 135 | 136 | elif func == 0x24: 137 | # And 138 | reg[r3] = reg[r1] & reg[r2] 139 | 140 | elif func == 0x1A: 141 | # Div 142 | a = get_signed(reg[r1]) 143 | b = get_signed(reg[r2]) 144 | if b == 0: 145 | raise SimulationException('PC = %d: Divided by zero when performing the div operation.' % (pc)) 146 | lo = (a // b) & 0xFFFFFFFF 147 | hi = (a % b) & 0xFFFFFFFF 148 | 149 | elif func == 0x1B: 150 | # Divu 151 | lo = (reg[r1] // reg[r2]) & 0xFFFFFFFF 152 | hi = (reg[r1] % reg[r2]) & 0xFFFFFFFF 153 | 154 | elif func == 0x09: 155 | # Jalr 156 | tmp = reg[r1] 157 | reg[r3] = pc + 4 158 | pc = tmp 159 | return 160 | 161 | elif func == 0x08: 162 | # Jr 163 | pc = reg[r1] 164 | return 165 | 166 | elif func == 0x10: 167 | # Mfhi 168 | reg[r3] = hi 169 | 170 | elif func == 0x12: 171 | # Mflo 172 | reg[r3] = lo 173 | 174 | elif func == 0x11: 175 | # Mthi 176 | hi = reg[r1] 177 | 178 | elif func == 0x13: 179 | # Mtlo 180 | lo = reg[r1] 181 | 182 | elif func == 0x18: 183 | # Mult 184 | a = get_signed(reg[r1]) 185 | b = get_signed(reg[r2]) 186 | hi = ((a*b)>>32) & 0xFFFFFFFF 187 | lo = (a*b) & 0xFFFFFFFF 188 | 189 | elif func == 0x19: 190 | # Multu 191 | hi = ((reg[r1]*reg[r2])>>32) & 0xFFFFFFFF 192 | lo = (reg[r1]*reg[r2]) & 0xFFFFFFFF 193 | 194 | elif func == 0x27: 195 | # Nor 196 | reg[r3] = ~(reg[r1] | reg[r2]) & 0xFFFFFFFF 197 | 198 | elif func == 0x25: 199 | # Or 200 | reg[r3] = reg[r1] | reg[r2] 201 | 202 | elif func == 0x00: 203 | # Sll 204 | reg[r3] = (reg[r2] << sa) & 0xFFFFFFFF 205 | 206 | elif func == 0x04: 207 | # Sllv 208 | reg[r3] = (reg[r2] << min(reg[r1], 32)) & 0xFFFFFFFF 209 | 210 | elif func == 0x2A: 211 | # Slt 212 | if get_signed(reg[r1]) < get_signed(reg[r2]): 213 | reg[r3] = 1 214 | else: 215 | reg[r3] = 0 216 | 217 | elif func == 0x2B: 218 | # Sltu 219 | if reg[r1] < reg[r2]: 220 | reg[r3] = 1 221 | else: 222 | reg[r3] = 0 223 | 224 | elif func == 0x03: 225 | # Sra 226 | s = reg[r2] >> 31 227 | reg[r3] = reg[r2] >> sa 228 | if s == 1: 229 | reg[r3] |= ((1<> 31 234 | t = min(reg[r1], 32) 235 | reg[r3] = reg[r2] >> t 236 | if s == 1: 237 | reg[r3] |= ((1<> sa 242 | 243 | elif func == 0x06: 244 | # Srlv 245 | reg[r3] = reg[r2] >> min(reg[r1], 32) 246 | 247 | elif func == 0x22: 248 | # Sub 249 | reg[r3] = (reg[r1] - reg[r2]) & 0xFFFFFFFF 250 | if (reg[r1]>>31) == (reg[r2]>>31) and (reg[r1]>>31) != (reg[r3]>>31): 251 | program_end = True 252 | raise SimulationException('PC = %d: An overflow occurs when performing the sub operation.' % (pc)) 253 | 254 | elif func == 0x23: 255 | # Subu 256 | reg[r3] = (reg[r1] - reg[r2]) & 0xFFFFFFFF 257 | 258 | elif func == 0x0C: 259 | # Syscall 260 | if reg[2] == 1: 261 | # Print integer 262 | output_data += str(get_signed(reg[4])) 263 | 264 | elif reg[2] == 4: 265 | # Print string 266 | output_data += read_string(reg[4]) 267 | 268 | elif reg[2] == 5: 269 | # Read integer 270 | if len(input_data) > 0: 271 | try: 272 | reg[2] = int(input_data[0]) & 0xFFFFFFFF 273 | input_data = input_data[1:] 274 | except: 275 | pass 276 | else: 277 | raise SimulationInfoException('PC = %d: Waiting for integer input. Simulation paused.' % (pc)) 278 | 279 | elif reg[2] == 8: 280 | # Read string 281 | if len(input_data) > 0: 282 | write_string(input_data[0], reg[4], reg[5]) 283 | input_data = input_data[1:] 284 | else: 285 | raise SimulationInfoException('PC = %d: Waiting for string input. Simulation paused.' % (pc)) 286 | 287 | elif reg[2] == 10: 288 | # Exit 289 | program_end = True 290 | return 291 | 292 | elif reg[2] == 11: 293 | # Print char 294 | output_data += chr(reg[4] & 0xFF) 295 | 296 | elif reg[2] == 12: 297 | # Read char 298 | if len(input_data) > 0: 299 | reg[4] = ord(input_data[0][0]) 300 | input_data = input_data[1:] 301 | else: 302 | raise SimulationInfoException('PC = %d: Waiting for char input. Simulation paused.' % (pc)) 303 | 304 | elif reg[2] == 30: 305 | # System time 306 | millis = int(round(time.time() * 1000)) 307 | reg[4] = millis & 0xFFFFFFFF 308 | reg[5] = (millis>>32) & 0xFFFFFFFF 309 | 310 | elif reg[2] == 41: 311 | # Random int 312 | reg[4] = random.randint(0, 0xFFFFFFFF) 313 | 314 | elif reg[2] == 42: 315 | # Random int with range 316 | reg[4] = random.randint(0, reg[5]-1) 317 | 318 | else: 319 | raise SimulationException('PC = %d: Unsupported syscall operation %d.' % (pc, reg[2])) 320 | 321 | elif func == 0x26: 322 | # Xor 323 | reg[r3] = reg[r1] ^ reg[r2] 324 | 325 | elif op == 0x08: 326 | # Addi 327 | t = get_signed(imm, 16) & 0xFFFFFFFF 328 | reg[r2] = (reg[r1] + t) & 0xFFFFFFFF 329 | if (reg[r1]>>31) == (t>>31) and (reg[r1]>>31) != (reg[r2]>>31): 330 | program_end = True 331 | raise SimulationException('PC = %d: An overflow occurs when performing the addi operation.' % (pc)) 332 | 333 | elif op == 0x09: 334 | # Addiu 335 | reg[r2] = (reg[r1] + get_signed(imm, 16)) & 0xFFFFFFFF 336 | 337 | elif op == 0x0C: 338 | # Andi 339 | reg[r2] = reg[r1] & imm 340 | 341 | elif op == 0x04: 342 | # Beq 343 | if reg[r1] == reg[r2]: 344 | pc += get_signed(imm, 16) << 2 345 | 346 | elif op == 0x01 and r2 == 1: 347 | # Bgez 348 | if get_signed(reg[r1]) >= 0: 349 | pc += get_signed(imm, 16) << 2 350 | 351 | elif op == 0x01 and r2 == 17: 352 | # Bgezal 353 | if get_signed(reg[r1]) >= 0: 354 | reg[31] = pc+4 355 | pc += get_signed(imm, 16) << 2 356 | 357 | elif op == 0x07 and r2 == 0: 358 | # Bgtz 359 | if get_signed(reg[r1]) > 0: 360 | pc += get_signed(imm, 16) << 2 361 | 362 | elif op == 0x06 and r2 == 0: 363 | # Blez 364 | if get_signed(reg[r1]) <= 0: 365 | pc += get_signed(imm, 16) << 2 366 | 367 | elif op == 0x01 and r2 == 0: 368 | # Bltz 369 | if get_signed(reg[r1]) < 0: 370 | pc += get_signed(imm, 16) << 2 371 | 372 | elif op == 0x01 and r2 == 16: 373 | # Bltzal 374 | if get_signed(reg[r1]) < 0: 375 | reg[31] = pc+4 376 | pc += get_signed(imm, 16) << 2 377 | 378 | elif op == 0x05: 379 | # Bne 380 | if reg[r1] != reg[r2]: 381 | pc += get_signed(imm, 16) << 2 382 | 383 | elif op == 0x02: 384 | # J 385 | pc = (pc&0xF0000000) | (lbl<<2) 386 | return 387 | 388 | elif op == 0x03: 389 | # Jal 390 | reg[31] = pc+4 391 | pc = (pc&0xF0000000) | (lbl<<2) 392 | return 393 | 394 | elif op == 0x20: 395 | # Lb 396 | addr = reg[r1] + get_signed(imm, 16) 397 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 398 | program_end = True 399 | raise SimulationException('PC = %d: Trying to load signed byte out of data segment.' % (pc)) 400 | reg[r2] = int(mem[addr]) 401 | 402 | if (reg[r2]>>7) == 1: 403 | reg[r2] |= 0xFFFFFF00 404 | 405 | elif op == 0x24: 406 | # Lbu 407 | addr = reg[r1] + get_signed(imm, 16) 408 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 409 | program_end = True 410 | raise SimulationException('PC = %d: Trying to load unsigned byte out of data segment.' % (pc)) 411 | reg[r2] = int(mem[addr]) 412 | 413 | elif op == 0x21: 414 | # Lh 415 | addr = reg[r1] + get_signed(imm, 16) 416 | if addr < config.DATA_ADDR_LOWER or addr+1 > config.DATA_ADDR_UPPER: 417 | program_end = True 418 | raise SimulationException('PC = %d: Trying to load signed half word out of data segment.' % (pc)) 419 | reg[r2] = (int(mem[addr])<<8) | int(mem[addr+1]) 420 | 421 | if (reg[r2]>>15) == 1: 422 | reg[r2] |= 0xFFFF0000 423 | 424 | elif op == 0x25: 425 | # Lhu 426 | addr = reg[r1] + get_signed(imm, 16) 427 | if addr < config.DATA_ADDR_LOWER or addr+1 > config.DATA_ADDR_UPPER: 428 | program_end = True 429 | raise SimulationException('PC = %d: Trying to load unsigned half word out of data segment.' % (pc)) 430 | reg[r2] = (int(mem[addr])<<8) | int(mem[addr+1]) 431 | 432 | elif op == 0x0F: 433 | # Lui 434 | reg[r2] = imm << 16 435 | 436 | elif op == 0x23: 437 | # Lw 438 | addr = reg[r1] + get_signed(imm, 16) 439 | if addr < config.DATA_ADDR_LOWER or addr+3 > config.DATA_ADDR_UPPER: 440 | program_end = True 441 | raise SimulationException('PC = %d: Trying to load word out of data segment.' % (pc)) 442 | reg[r2] = (int(mem[addr])<<24) | (int(mem[addr+1])<<16) | (int(mem[addr+2])<<8) | int(mem[addr+3]) 443 | 444 | elif op == 0x0D: 445 | # Ori 446 | reg[r2] = reg[r1] | imm 447 | 448 | elif op == 0x28: 449 | # Sb 450 | addr = reg[r1] + get_signed(imm, 16) 451 | if addr < config.DATA_ADDR_LOWER or addr > config.DATA_ADDR_UPPER: 452 | program_end = True 453 | raise SimulationException('PC = %d: Trying to store byte out of data segment.' % (pc)) 454 | mem[addr] = reg[r2] & 0xFF 455 | 456 | elif op == 0x0A: 457 | # Slti 458 | if get_signed(reg[r1]) < get_signed(imm, 16): 459 | reg[r2] = 1 460 | else: 461 | reg[r2] = 0 462 | 463 | elif op == 0x0B: 464 | # Sltiu 465 | if reg[r1] < imm: 466 | reg[r2] = 1 467 | else: 468 | reg[r2] = 0 469 | 470 | elif op == 0x29: 471 | # Sh 472 | addr = reg[r1] + get_signed(imm, 16) 473 | if addr < config.DATA_ADDR_LOWER or addr+1 > config.DATA_ADDR_UPPER: 474 | program_end = True 475 | raise SimulationException('PC = %d: Trying to store half word out of data segment.' % (pc)) 476 | mem[addr] = (reg[r2]>>8) & 0xFF 477 | mem[addr+1] = reg[r2] & 0xFF 478 | 479 | elif op == 0x2B: 480 | # Sw 481 | addr = reg[r1] + get_signed(imm, 16) 482 | if addr < config.DATA_ADDR_LOWER or addr+3 > config.DATA_ADDR_UPPER: 483 | program_end = True 484 | raise SimulationException('PC = %d: Trying to store word out of data segment.' % (pc)) 485 | mem[addr] = reg[r2]>>24 486 | mem[addr+1] = (reg[r2]>>16) & 0xFF 487 | mem[addr+2] = (reg[r2]>>8) & 0xFF 488 | mem[addr+3] = reg[r2] & 0xFF 489 | 490 | elif op == 0x0E: 491 | # Xori 492 | reg[r2] = reg[r1] ^ imm 493 | 494 | else: 495 | SimulationException('PC = %d: Unsupported instruction.' % (pc)) 496 | 497 | pc += 4 498 | 499 | # ------------------------------------------------------------------------------ 500 | # Run all the program 501 | # ------------------------------------------------------------------------------ 502 | def run(): 503 | global program_end 504 | 505 | for i in range(0, STEP_LIMIT): 506 | if program_end: 507 | return 508 | step() 509 | 510 | program_end = True 511 | raise SimulationException('PC = %d: Too many instructions executed.' % (pc)) 512 | 513 | # ------------------------------------------------------------------------------ 514 | # Get output data of the simulator 515 | # ------------------------------------------------------------------------------ 516 | def get_output(): 517 | global output_data 518 | 519 | for c in output_data: 520 | if ord(c) >= 33 and ord(c) <= 126: 521 | ret = output_data 522 | output_data = '' 523 | return ret 524 | return '' 525 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/bin_browser.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QSize 2 | from PyQt5.QtGui import QFont, QTextCursor 3 | from PyQt5.QtWidgets import QTextBrowser 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Binary code browser 7 | # ------------------------------------------------------------------------------ 8 | class BinBrowser(QTextBrowser): 9 | 10 | def __init__(self, parent = None): 11 | super(BinBrowser, self).__init__(parent) 12 | 13 | self.init_pc = 0 14 | self.mem = bytearray() 15 | 16 | # Set layout 17 | font = QFont('Consolas', 11) 18 | font.setFixedPitch(True) 19 | 20 | self.setFont(font) 21 | self.setStyleSheet( 22 | 'background-color: rgb(41, 49, 52);' \ 23 | 'color: rgb(224, 226, 228);' 24 | ) 25 | 26 | # -------------------------------------------------------------------------- 27 | # Set size of the widget 28 | # -------------------------------------------------------------------------- 29 | def sizeHint(self): 30 | return QSize(320, 0) 31 | 32 | # -------------------------------------------------------------------------- 33 | # Display binary code 34 | # -------------------------------------------------------------------------- 35 | def showBin(self, mem, pc = -1): 36 | self.mem = mem 37 | 38 | line_num = 0 39 | highlighted_line_num = 0 40 | 41 | # Set style 42 | html = \ 43 | '' \ 49 | '' 50 | 51 | zero = False 52 | for i in range(0, len(mem), 4): 53 | if mem[i] == 0 and mem[i+1] == 0 and mem[i+2] == 0 and mem[i+3] == 0: 54 | if not zero: 55 | line_num += 1 56 | html += '' 57 | for t in range(0, 11): 58 | html += '' 59 | html += '' 60 | zero = True 61 | else: 62 | # Highlight current line if PC points to here 63 | line_num += 1 64 | if i == pc: 65 | highlighted_line_num = line_num 66 | html += '' 67 | else: 68 | html += '' 69 | 70 | # Parse invisible characters to '.' 71 | char_lst = list(map(lambda x: 46 if x < 33 or x > 126 else x, mem[i:i+4])) 72 | html += \ 73 | '' \ 74 | '' \ 75 | '' \ 76 | '' \ 77 | '' \ 78 | '' \ 79 | '' \ 80 | '' \ 81 | '' \ 82 | '' \ 83 | '' \ 84 | '' \ 85 | % (i, *mem[i:i+4], *char_lst) 86 | zero = False 87 | html += '
%08X %02x%02x%02x%02x &#%d;&#%d;&#%d;&#%d;
' 88 | 89 | self.setHtml(html) 90 | 91 | # Move to highlighted line 92 | if highlighted_line_num > 0: 93 | self.moveCursor(QTextCursor.End) 94 | cursor = QTextCursor(self.document().findBlockByLineNumber(highlighted_line_num*11)) 95 | self.setTextCursor(cursor) 96 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/main_window.py: -------------------------------------------------------------------------------- 1 | import re 2 | from PyQt5.QtCore import Qt 3 | from PyQt5.QtGui import QIcon, QTextCursor 4 | from PyQt5.QtWidgets import QApplication, QFileDialog, QMainWindow, QMenu, QMessageBox, QDockWidget 5 | from com.tsreaper.yame.ui.mips_editor import MipsEditor 6 | from com.tsreaper.yame.ui.mips_highlighter import MipsHighlighter 7 | from com.tsreaper.yame.ui.message_browser import MessageBrowser 8 | from com.tsreaper.yame.ui.bin_browser import BinBrowser 9 | from com.tsreaper.yame.ui.simulator_window import SimulatorWindow 10 | from com.tsreaper.yame.ui.setting_dialog import SettingDialog 11 | from com.tsreaper.yame.ui.pseudo_dialog import PseudoDialog 12 | 13 | import com.tsreaper.yame.assemble.assembler as assembler 14 | import com.tsreaper.yame.assemble.pseudo as pseudo 15 | import com.tsreaper.yame.disassemble.disassembler as disassembler 16 | import com.tsreaper.yame.simulate.simulator as simulator 17 | import com.tsreaper.yame.constant.config as config 18 | import com.tsreaper.yame.io.coe_file as coe_file 19 | import com.tsreaper.yame.io.bin_file as bin_file 20 | import com.tsreaper.yame.constant.config as config 21 | 22 | # ------------------------------------------------------------------------------ 23 | # YAME's main window 24 | # ------------------------------------------------------------------------------ 25 | class MainWindow(QMainWindow): 26 | 27 | def __init__(self, parent = None): 28 | super(MainWindow, self).__init__(parent) 29 | 30 | self.initAssembler() 31 | self.initWindow() 32 | 33 | # -------------------------------------------------------------------------- 34 | # Initialize assembler 35 | # -------------------------------------------------------------------------- 36 | def initAssembler(self): 37 | # Load config and pseudo instructions 38 | pseudo.load_pseudo(config.CONFIG_FILE) 39 | config.load_config(config.CONFIG_FILE) 40 | 41 | # -------------------------------------------------------------------------- 42 | # Initialize main window 43 | # -------------------------------------------------------------------------- 44 | def initWindow(self): 45 | self.currentFile = '' 46 | self.dirty = False 47 | 48 | self.setupFileMenu() 49 | self.setupSettingMenu() 50 | self.setupAssembleMenu() 51 | self.setupHelpMenu() 52 | self.setupEditor() 53 | self.setupDockable() 54 | 55 | self.setCentralWidget(self.editor) 56 | self.setWindowTitle('Untitled - YAME') 57 | self.setWindowIcon(QIcon('icons/yame.png')) 58 | 59 | self.simulatorWindow = SimulatorWindow(self) 60 | 61 | # -------------------------------------------------------------------------- 62 | # Set when text in the editor is modified 63 | # -------------------------------------------------------------------------- 64 | def setDirty(self): 65 | self.dirty = True 66 | self.updateStatus() 67 | 68 | # -------------------------------------------------------------------------- 69 | # Clear dirty state 70 | # -------------------------------------------------------------------------- 71 | def clearDirty(self): 72 | self.dirty = False 73 | self.updateStatus() 74 | 75 | # -------------------------------------------------------------------------- 76 | # Check if text in the editor is modified 77 | # -------------------------------------------------------------------------- 78 | def isDirty(self): 79 | return self.dirty and (self.currentFile or self.editor.toPlainText()) 80 | 81 | # -------------------------------------------------------------------------- 82 | # YAME's main window 83 | # -------------------------------------------------------------------------- 84 | def updateStatus(self): 85 | title = '* ' if self.isDirty() else '' 86 | if self.currentFile: 87 | title += self.currentFile 88 | else: 89 | title += 'Untitled' 90 | title += ' - YAME' 91 | self.setWindowTitle(title) 92 | 93 | # -------------------------------------------------------------------------- 94 | # Confirm message box 95 | # -------------------------------------------------------------------------- 96 | def okToContinue(self): 97 | if self.isDirty(): 98 | reply = QMessageBox.question( 99 | self, 'YAME - 尚未保存的修改', 100 | '

存在尚未保存的修改,是否保存?

', 101 | QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel 102 | ) 103 | if reply == QMessageBox.Cancel: 104 | return False 105 | elif reply == QMessageBox.Yes: 106 | return self.saveFile() 107 | return True 108 | 109 | # -------------------------------------------------------------------------- 110 | # Quit the app 111 | # -------------------------------------------------------------------------- 112 | def closeEvent(self, event): 113 | if self.okToContinue(): 114 | # Save config and pseudo instructions 115 | pseudo.save_pseudo(config.CONFIG_FILE) 116 | config.save_config(config.CONFIG_FILE) 117 | 118 | event.accept() 119 | else: 120 | event.ignore() 121 | 122 | # -------------------------------------------------------------------------- 123 | # Assemble text to machine code 124 | # -------------------------------------------------------------------------- 125 | def assemble(self): 126 | self.msgBrowser.clear() 127 | self.bottomDock.show() 128 | 129 | self.msgBrowser.append('Assembling...') 130 | try: 131 | mem, init_pc = assembler.assemble(self.editor.toPlainText()) 132 | except Exception as e: 133 | # Move editor to the corresponding line 134 | regex_res = re.search('Line ([0-9]+?):', str(e)) 135 | if regex_res != None: 136 | line_num = int(regex_res.group(1)) 137 | self.editor.moveCursor(QTextCursor.End) 138 | cursor = QTextCursor(self.editor.document().findBlockByLineNumber(line_num-1)) 139 | self.editor.setTextCursor(cursor) 140 | 141 | self.msgBrowser.append('[ERROR] %s' % (str(e))) 142 | self.msgBrowser.append('Assembling procedure terminated.') 143 | return False 144 | else: 145 | self.msgBrowser.append('Assembling procedure finished.') 146 | self.binBrowser.init_pc = init_pc 147 | self.binBrowser.showBin(mem) 148 | self.rightDock.show() 149 | return True 150 | 151 | # -------------------------------------------------------------------------- 152 | # Disassemble machine code to text 153 | # -------------------------------------------------------------------------- 154 | def disassemble(self): 155 | if not self.okToContinue(): 156 | return False 157 | 158 | self.msgBrowser.clear() 159 | self.bottomDock.show() 160 | self.rightDock.show() 161 | 162 | self.msgBrowser.append('Disassembling...') 163 | try: 164 | text = disassembler.disassemble(self.binBrowser.mem) 165 | except Exception as e: 166 | self.msgBrowser.append('[ERROR] %s' % (str(e))) 167 | self.msgBrowser.append('Disassembling procedure terminated.') 168 | return False 169 | else: 170 | self.msgBrowser.append('Disassembling procedure finished.') 171 | self.currentFile = '' 172 | self.editor.setPlainText(text) 173 | return True 174 | 175 | # -------------------------------------------------------------------------- 176 | # Simulate MIPS instructions 177 | # -------------------------------------------------------------------------- 178 | def simulate(self): 179 | if not self.assemble(): 180 | return False 181 | 182 | self.simulatorWindow.loadSimulator(self.binBrowser.mem, self.binBrowser.init_pc) 183 | self.simulatorWindow.show() 184 | return True 185 | 186 | # -------------------------------------------------------------------------- 187 | # Pesudo instructions management 188 | # -------------------------------------------------------------------------- 189 | def pseudoIns(self): 190 | pseudoDialog = PseudoDialog(self) 191 | pseudoDialog.exec_() 192 | 193 | # Update highlighter 194 | self.highlighter = MipsHighlighter(self.editor.document()) 195 | 196 | # -------------------------------------------------------------------------- 197 | # Setting memory size, text segment address, etc. 198 | # -------------------------------------------------------------------------- 199 | def setting(self): 200 | settingDialog = SettingDialog(self) 201 | settingDialog.exec_() 202 | 203 | # -------------------------------------------------------------------------- 204 | # About message box 205 | # -------------------------------------------------------------------------- 206 | def about(self): 207 | QMessageBox.about( 208 | self, '关于 YAME', 209 | '

YAME: A MIPS Editor

'\ 210 | '

Made by TsReaper

'\ 211 | '

YAME 是一个简单的 MIPS 编辑器 / 汇编器 / 反汇编器 / 模拟器。

' 212 | '

工程 github 页面:https://github.com/TsReaper/YAME-A-MIPS-Editor

' 213 | ) 214 | 215 | # -------------------------------------------------------------------------- 216 | # Clear editor 217 | # -------------------------------------------------------------------------- 218 | def newFile(self): 219 | if not self.okToContinue(): 220 | return False 221 | 222 | self.editor.clear() 223 | self.currentFile = '' 224 | self.clearDirty() 225 | return True 226 | 227 | # -------------------------------------------------------------------------- 228 | # Open a file in the disk 229 | # -------------------------------------------------------------------------- 230 | def openFile(self): 231 | if not self.okToContinue(): 232 | return False 233 | 234 | path, _ = QFileDialog.getOpenFileName( 235 | self, '打开', '', '汇编文件 (*.asm)' 236 | ) 237 | 238 | if path: 239 | try: 240 | # Try to use gbk decoder 241 | file = open(path, 'r') 242 | self.editor.setPlainText(file.read()) 243 | except: 244 | # Try to use utf-8 decoder 245 | file.close() 246 | file = open(path, 'r', encoding = 'utf-8') 247 | self.editor.setPlainText(file.read()) 248 | file.close() 249 | 250 | self.currentFile = path 251 | self.clearDirty() 252 | return True 253 | 254 | return False 255 | 256 | # -------------------------------------------------------------------------- 257 | # Save current text 258 | # -------------------------------------------------------------------------- 259 | def saveFile(self): 260 | if not self.currentFile: 261 | return self.saveAnotherFile() 262 | 263 | file = open(self.currentFile, 'w') 264 | file.write(self.editor.toPlainText()) 265 | file.close() 266 | 267 | self.clearDirty() 268 | return True 269 | 270 | # -------------------------------------------------------------------------- 271 | # Save copy of current text 272 | # -------------------------------------------------------------------------- 273 | def saveAnotherFile(self): 274 | path, _ = QFileDialog.getSaveFileName( 275 | self, '另存为', self.currentFile if self.currentFile else '', '汇编文件 (*.asm)' 276 | ) 277 | 278 | if path: 279 | file = open(path, 'w') 280 | file.write(self.editor.toPlainText()) 281 | file.close() 282 | 283 | self.currentFile = path 284 | self.clearDirty() 285 | return True 286 | 287 | return False 288 | 289 | # -------------------------------------------------------------------------- 290 | # Import coe or bin file 291 | # -------------------------------------------------------------------------- 292 | def importFile(self): 293 | self.newFile() 294 | 295 | path, _ = QFileDialog.getOpenFileName( 296 | self, '导入', '', 'COE文件 (*.coe);;二进制文件 (*.bin)' 297 | ) 298 | 299 | try: 300 | if path.split('.')[-1] == 'coe': 301 | mem = coe_file.read(path) 302 | elif path.split('.')[-1] == 'bin': 303 | mem = bin_file.read(path) 304 | else: 305 | return False 306 | except Exception as e: 307 | self.msgBrowser.clear() 308 | self.bottomDock.show() 309 | self.msgBrowser.append('[ERROR] %s' % (str(e))) 310 | return False 311 | 312 | self.rightDock.show() 313 | self.binBrowser.showBin(mem) 314 | 315 | return self.disassemble() 316 | 317 | # -------------------------------------------------------------------------- 318 | # Export coe or bin file 319 | # -------------------------------------------------------------------------- 320 | def exportFile(self): 321 | if not self.assemble(): 322 | return False 323 | 324 | path, _ = QFileDialog.getSaveFileName( 325 | self, '导出', '', 'COE文件 (*.coe);;二进制文件 (*.bin)' 326 | ) 327 | 328 | if path.split('.')[-1] == 'coe': 329 | coe_file.write(path, self.binBrowser.mem) 330 | return True 331 | if path.split('.')[-1] == 'bin': 332 | bin_file.write(path, self.binBrowser.mem) 333 | return True 334 | 335 | return False 336 | 337 | # -------------------------------------------------------------------------- 338 | # Setup file menu 339 | # -------------------------------------------------------------------------- 340 | def setupFileMenu(self): 341 | fileMenu = QMenu('文件(&F)', self) 342 | self.menuBar().addMenu(fileMenu) 343 | 344 | fileMenu.addAction('新建(&N)', self.newFile, 'Ctrl+N') 345 | fileMenu.addAction('打开(&O)...', self.openFile, 'Ctrl+O') 346 | fileMenu.addAction('保存(&S)', self.saveFile, 'Ctrl+S') 347 | fileMenu.addAction('另存为(&A)...', self.saveAnotherFile, 'Ctrl+Alt+S') 348 | fileMenu.addAction('导入(&I)...', self.importFile, 'Ctrl+I') 349 | fileMenu.addAction('导出(&E)...', self.exportFile, 'Ctrl+E') 350 | fileMenu.addAction('退出(&X)', self.close, 'Alt+F4') 351 | 352 | # -------------------------------------------------------------------------- 353 | # Setup setting menu 354 | # -------------------------------------------------------------------------- 355 | def setupSettingMenu(self): 356 | settingMenu = QMenu('设置(&S)', self) 357 | self.menuBar().addMenu(settingMenu) 358 | 359 | settingMenu.addAction('汇编器设置', self.setting, 'F6') 360 | settingMenu.addAction('伪指令', self.pseudoIns, 'F7') 361 | 362 | # -------------------------------------------------------------------------- 363 | # Setup assemble menu 364 | # -------------------------------------------------------------------------- 365 | def setupAssembleMenu(self): 366 | assembleMenu = QMenu('汇编(&A)', self) 367 | self.menuBar().addMenu(assembleMenu) 368 | 369 | assembleMenu.addAction('汇编(&A)', self.assemble, 'F9') 370 | assembleMenu.addAction('反汇编(&D)', self.disassemble, 'F10') 371 | assembleMenu.addAction('模拟(&S)', self.simulate, 'F11') 372 | 373 | # -------------------------------------------------------------------------- 374 | # Setup help menu 375 | # -------------------------------------------------------------------------- 376 | def setupHelpMenu(self): 377 | helpMenu = QMenu("帮助(&H)", self) 378 | self.menuBar().addMenu(helpMenu) 379 | 380 | helpMenu.addAction('关于(&A)', self.about) 381 | 382 | # -------------------------------------------------------------------------- 383 | # Setup mips editor 384 | # -------------------------------------------------------------------------- 385 | def setupEditor(self): 386 | self.editor = MipsEditor() 387 | self.editor.textChanged.connect(self.setDirty) 388 | self.highlighter = MipsHighlighter(self.editor.document()) 389 | 390 | # -------------------------------------------------------------------------- 391 | # Setup dockable widgets 392 | # -------------------------------------------------------------------------- 393 | def setupDockable(self): 394 | self.msgBrowser = MessageBrowser() 395 | self.bottomDock = QDockWidget('信息', self) 396 | self.bottomDock.setFeatures(QDockWidget.DockWidgetClosable) 397 | self.bottomDock.setWidget(self.msgBrowser) 398 | self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock) 399 | self.bottomDock.hide() 400 | 401 | self.binBrowser = BinBrowser() 402 | self.rightDock = QDockWidget('机器码', self) 403 | self.rightDock.setFeatures(QDockWidget.DockWidgetClosable) 404 | self.rightDock.setWidget(self.binBrowser) 405 | self.addDockWidget(Qt.RightDockWidgetArea, self.rightDock) 406 | self.rightDock.hide() 407 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/message_browser.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QSize 2 | from PyQt5.QtGui import QFont 3 | from PyQt5.QtWidgets import QTextBrowser 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Message Window 7 | # ------------------------------------------------------------------------------ 8 | class MessageBrowser(QTextBrowser): 9 | 10 | def __init__(self, parent = None): 11 | super(MessageBrowser, self).__init__(parent) 12 | 13 | # Set style 14 | font = QFont('Consolas', 11) 15 | font.setFixedPitch(True) 16 | 17 | self.setFont(font) 18 | self.setStyleSheet( 19 | 'background-color: rgb(41, 49, 52);' \ 20 | 'color: rgb(224, 226, 228);' 21 | ) 22 | 23 | # -------------------------------------------------------------------------- 24 | # Set size of the widget 25 | # -------------------------------------------------------------------------- 26 | def sizeHint(self): 27 | return QSize(0, 100) 28 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/mips_editor.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt, QSize, QRect 2 | from PyQt5.QtGui import QFont, QColor, QPainter, QTextFormat 3 | from PyQt5.QtWidgets import QWidget, QTextEdit, QPlainTextEdit 4 | 5 | class LineNumberArea(QWidget): 6 | 7 | def __init__(self, parent): 8 | super(LineNumberArea, self).__init__(parent) 9 | self.editor = parent 10 | 11 | def sizeHint(self): 12 | return QSize(self.editor.lineNumberAreaWidth(), 0) 13 | 14 | def paintEvent(self, event): 15 | self.editor.lineNumberAreaPaintEvent(event) 16 | 17 | class MipsEditor(QPlainTextEdit): 18 | 19 | def __init__(self, parent = None): 20 | super(MipsEditor, self).__init__(parent) 21 | 22 | font = QFont('Consolas', 11) 23 | font.setFixedPitch(True) 24 | 25 | self.setFont(font) 26 | self.setStyleSheet( 27 | 'background-color: rgb(41, 49, 52);' \ 28 | 'color: rgb(224, 226, 228);' 29 | ) 30 | 31 | self.setLineWrapMode(QPlainTextEdit.NoWrap) 32 | 33 | self.lineNumberArea = LineNumberArea(self) 34 | 35 | self.blockCountChanged.connect(self.updateLineNumberAreaWidth) 36 | self.updateRequest.connect(self.updateLineNumberArea) 37 | self.cursorPositionChanged.connect(self.highlightCurrentLine) 38 | 39 | self.updateLineNumberAreaWidth(0) 40 | 41 | def lineNumberAreaWidth(self): 42 | digits = 0 43 | count = max(1, self.blockCount()) 44 | while count > 0: 45 | count = count//10 46 | digits += 1 47 | return self.fontMetrics().width('9') * digits + 15 48 | 49 | def updateLineNumberAreaWidth(self, _): 50 | self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) 51 | 52 | def updateLineNumberArea(self, rect, dy): 53 | if dy: 54 | self.lineNumberArea.scroll(0, dy) 55 | else: 56 | self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) 57 | 58 | if rect.contains(self.viewport().rect()): 59 | self.updateLineNumberAreaWidth(0) 60 | 61 | def resizeEvent(self, event): 62 | super().resizeEvent(event) 63 | 64 | cr = self.contentsRect(); 65 | self.lineNumberArea.setGeometry( 66 | QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()) 67 | ) 68 | 69 | def lineNumberAreaPaintEvent(self, event): 70 | painter = QPainter(self.lineNumberArea) 71 | painter.fillRect(event.rect(), QColor(63, 75, 78)) 72 | 73 | font = QFont('Consolas', 11) 74 | font.setFixedPitch(True) 75 | 76 | painter.setFont(font) 77 | painter.setPen(QColor(129, 150, 154)) 78 | 79 | block = self.firstVisibleBlock() 80 | blockNumber = block.blockNumber() 81 | top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() 82 | bottom = top + self.blockBoundingRect(block).height() 83 | 84 | height = self.fontMetrics().height() 85 | while block.isValid() and (top <= event.rect().bottom()): 86 | if block.isVisible() and (bottom >= event.rect().top()): 87 | number = str(blockNumber + 1) 88 | painter.drawText( 89 | 0, top, self.lineNumberArea.width(), height, 90 | Qt.AlignCenter, number 91 | ) 92 | 93 | block = block.next() 94 | top = bottom 95 | bottom = top + self.blockBoundingRect(block).height() 96 | blockNumber += 1 97 | 98 | def highlightCurrentLine(self): 99 | extraSelections = [] 100 | 101 | if not self.isReadOnly(): 102 | selection = QTextEdit.ExtraSelection() 103 | lineColor = QColor(47, 57, 60) 104 | 105 | selection.format.setBackground(lineColor) 106 | selection.format.setProperty(QTextFormat.FullWidthSelection, True) 107 | selection.cursor = self.textCursor() 108 | selection.cursor.clearSelection() 109 | extraSelections.append(selection) 110 | 111 | self.setExtraSelections(extraSelections) 112 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/mips_highlighter.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QRegExp 2 | from PyQt5.QtGui import QFont, QColor, QSyntaxHighlighter, QTextCharFormat 3 | 4 | from com.tsreaper.yame.constant.instruction_template import * 5 | from com.tsreaper.yame.constant.register_list import * 6 | import com.tsreaper.yame.assemble.pseudo as pseudo 7 | 8 | # ------------------------------------------------------------------------------ 9 | # Mips highlighter 10 | # ------------------------------------------------------------------------------ 11 | class MipsHighlighter(QSyntaxHighlighter): 12 | 13 | def __init__(self, parent = None): 14 | super(MipsHighlighter, self).__init__(parent) 15 | 16 | # Set highlight rules 17 | self.highlightingRules = [] 18 | 19 | # Symbols 20 | sym = '@~\!\^&\*\(\)\-\+\=\|;<\,>/' 21 | symFormat = QTextCharFormat() 22 | symFormat.setForeground(QColor(232, 226, 183)) 23 | 24 | self.highlightingRules.append((QRegExp('[' + sym + ']'), symFormat)) 25 | 26 | # Immediates 27 | immFormat = QTextCharFormat() 28 | immFormat.setForeground(QColor(255, 205, 34)) 29 | 30 | self.highlightingRules.append((QRegExp(r'\b[0-9]\w*\b'), immFormat)) 31 | 32 | # Text instruction highlight format 33 | textInsFormat = QTextCharFormat() 34 | textInsFormat.setForeground(QColor(147, 199, 99)) 35 | 36 | textInsPatterns = [] 37 | for i in R_INSTRUCTIONS.keys(): 38 | textInsPatterns.append(r'\b' + i + r'\b') 39 | for i in I_INSTRUCTIONS.keys(): 40 | textInsPatterns.append(r'\b' + i + r'\b') 41 | for i in J_INSTRUCTIONS.keys(): 42 | textInsPatterns.append(r'\b' + i + r'\b') 43 | 44 | for pattern in textInsPatterns: 45 | self.highlightingRules.append((QRegExp(pattern), textInsFormat)) 46 | 47 | # Data instruction highlight format 48 | dataInsFormat = QTextCharFormat() 49 | dataInsFormat.setForeground(QColor(160, 130, 189)) 50 | 51 | dataInsPatterns = [r'\.text\b', r'\.data\b'] 52 | for i in DATA_INSTRUCTIONS.keys(): 53 | dataInsPatterns.append(i.replace('.', r'\.') + r'\b') 54 | 55 | for pattern in dataInsPatterns: 56 | self.highlightingRules.append((QRegExp(pattern), dataInsFormat)) 57 | 58 | # pseudo instructions 59 | pseudoInsFormat = QTextCharFormat() 60 | pseudoInsFormat.setForeground(QColor(0, 128, 192)) 61 | 62 | pseudoInsPatterns = [] 63 | for i in pseudo.pseudo_dict.keys(): 64 | pseudoInsPatterns.append(r'\b' + i + r'\b') 65 | 66 | for pattern in pseudoInsPatterns: 67 | self.highlightingRules.append((QRegExp(pattern), pseudoInsFormat)) 68 | 69 | # Parameters for pseudo instructions 70 | pseudoParamFormat = QTextCharFormat() 71 | pseudoParamFormat.setForeground(QColor(103, 140, 177)) 72 | 73 | qreg = QRegExp(r'\[[0-9]+\]') 74 | qreg.setMinimal(True) 75 | self.highlightingRules.append((qreg, pseudoParamFormat)) 76 | 77 | # Register highlight format 78 | regFormat = QTextCharFormat() 79 | regFormat.setForeground(QColor(103, 140, 177)) 80 | 81 | regPatterns = [] 82 | for r in REGISTER_LIST: 83 | regPatterns.append(r.replace('$', r'\$') + r'\b') 84 | for r in REGISTER_NUM_LIST: 85 | regPatterns.append(r.replace('$', r'\$') + r'\b') 86 | 87 | for pattern in regPatterns: 88 | self.highlightingRules.append((QRegExp(pattern), regFormat)) 89 | 90 | # Strings 91 | strFormat = QTextCharFormat() 92 | strFormat.setForeground(QColor(236, 118, 0)) 93 | 94 | qreg = QRegExp('".*"') 95 | qreg.setMinimal(True) 96 | self.highlightingRules.append((qreg, strFormat)) 97 | qreg = QRegExp('\'.*\'') 98 | qreg.setMinimal(True) 99 | self.highlightingRules.append((qreg, strFormat)) 100 | 101 | # Comments 102 | comFormat = QTextCharFormat() 103 | comFormat.setForeground(QColor(102, 116, 123)) 104 | 105 | self.highlightingRules.append((QRegExp('(#.*)|(//.*)'), comFormat)) 106 | qreg = QRegExp('(/\*.*\*/)') 107 | qreg.setMinimal(True) 108 | self.highlightingRules.append((qreg, comFormat)) 109 | 110 | # -------------------------------------------------------------------------- 111 | # Simple highlight method, ignoring multiple lines of comments 112 | # -------------------------------------------------------------------------- 113 | def highlightBlock(self, text): 114 | for pattern, format in self.highlightingRules: 115 | expression = QRegExp(pattern) 116 | index = expression.indexIn(text) 117 | while index >= 0: 118 | length = expression.matchedLength() 119 | self.setFormat(index, length, format) 120 | index = expression.indexIn(text, index + length) 121 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/pseudo_dialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QListWidget, QPushButton, QLabel, QLineEdit, QVBoxLayout, QHBoxLayout, QMessageBox 2 | from com.tsreaper.yame.ui.mips_editor import MipsEditor 3 | from com.tsreaper.yame.ui.mips_highlighter import MipsHighlighter 4 | 5 | import com.tsreaper.yame.assemble.pseudo as pseudo 6 | from com.tsreaper.yame.constant.instruction_template import * 7 | 8 | class PseudoDialog(QDialog): 9 | 10 | def __init__(self, parent = None): 11 | super(PseudoDialog, self).__init__(parent) 12 | self.initDialog() 13 | 14 | def initDialog(self): 15 | self.setWindowTitle('伪指令') 16 | 17 | self.addButton = QPushButton('添加') 18 | self.addButton.clicked.connect(self.addItem) 19 | self.delButton = QPushButton('删除') 20 | self.delButton.setEnabled(False) 21 | self.delButton.clicked.connect(self.delItem) 22 | self.saveButton = QPushButton('保存') 23 | self.saveButton.setEnabled(False) 24 | self.saveButton.clicked.connect(self.saveItem) 25 | 26 | self.pseudoNameLabel = QLabel('伪指令名') 27 | self.pseudoNameValue = QLineEdit() 28 | self.pseudoNameValue.setEnabled(False) 29 | self.operandNumberLabel = QLabel('操作数数量') 30 | self.operandNumberValue = QLineEdit() 31 | self.operandNumberValue.setEnabled(False) 32 | self.realInsLabel = QLabel('真指令') 33 | self.realInsValue = MipsEditor() 34 | self.realInsValue.setEnabled(False) 35 | self.highlighter = MipsHighlighter(self.realInsValue.document()) 36 | 37 | self.pseudoList = QListWidget(self) 38 | for op in pseudo.pseudo_dict.keys(): 39 | self.pseudoList.addItem(op) 40 | self.pseudoList.currentItemChanged.connect(self.changeItem) 41 | self.updateList('') 42 | 43 | buttonLayout = QVBoxLayout() 44 | buttonLayout.addWidget(self.addButton) 45 | buttonLayout.addWidget(self.delButton) 46 | buttonLayout.addWidget(self.saveButton) 47 | 48 | upperLayout = QHBoxLayout() 49 | upperLayout.addWidget(self.pseudoList) 50 | upperLayout.addLayout(buttonLayout) 51 | 52 | lowerLayout = QVBoxLayout() 53 | lowerLayout.addWidget(self.pseudoNameLabel) 54 | lowerLayout.addWidget(self.pseudoNameValue) 55 | lowerLayout.addWidget(self.operandNumberLabel) 56 | lowerLayout.addWidget(self.operandNumberValue) 57 | lowerLayout.addWidget(self.realInsLabel) 58 | lowerLayout.addWidget(self.realInsValue) 59 | 60 | layout = QVBoxLayout() 61 | layout.addLayout(upperLayout) 62 | layout.addLayout(lowerLayout) 63 | self.setLayout(layout) 64 | 65 | def updateList(self, itemName): 66 | self.pseudoList.sortItems() 67 | 68 | for i in range(0, self.pseudoList.count()): 69 | if self.pseudoList.item(i).text() == itemName: 70 | self.pseudoList.setCurrentRow(i) 71 | return 72 | 73 | self.pseudoList.setCurrentRow(0) 74 | 75 | def addItem(self): 76 | id = 0 77 | while ('pseudo' + str(id)) in pseudo.pseudo_dict.keys(): 78 | id += 1 79 | 80 | ins = 'pseudo' + str(id) 81 | pseudo.pseudo_dict[ins] = [0, ''] 82 | self.pseudoList.addItem(ins) 83 | self.updateList(ins) 84 | 85 | def delItem(self): 86 | ins = self.pseudoList.currentItem().text() 87 | pseudo.pseudo_dict.pop(ins) 88 | self.pseudoList.takeItem(self.pseudoList.currentRow()) 89 | self.updateList('') 90 | 91 | def saveItem(self): 92 | ins = self.pseudoList.currentItem().text() 93 | new_ins = self.pseudoNameValue.text() 94 | try: 95 | opNum = int(self.operandNumberValue.text()) 96 | except: 97 | opNum = 0 98 | real = self.realInsValue.toPlainText() 99 | 100 | if new_ins == '': 101 | QMessageBox.critical(self, '错误', '

伪指令名不能为空!

') 102 | return 103 | elif ins != new_ins and ( 104 | new_ins in pseudo.pseudo_dict.keys() or 105 | new_ins in R_INSTRUCTIONS.keys() or 106 | new_ins in I_INSTRUCTIONS.keys() or 107 | new_ins in J_INSTRUCTIONS.keys() 108 | ): 109 | QMessageBox.critical(self, '错误', '

伪指令名与已有指令或伪指令重复!

') 110 | return 111 | 112 | pseudo.pseudo_dict.pop(ins) 113 | self.pseudoList.takeItem(self.pseudoList.currentRow()) 114 | pseudo.pseudo_dict[new_ins] = [opNum, ';'.join(real.split('\n'))] 115 | self.pseudoList.addItem(new_ins) 116 | self.updateList(new_ins) 117 | 118 | def changeItem(self): 119 | if self.pseudoList.currentItem() == None: 120 | self.pseudoNameValue.clear() 121 | self.operandNumberValue.clear() 122 | self.realInsValue.clear() 123 | 124 | self.pseudoNameValue.setEnabled(False) 125 | self.operandNumberValue.setEnabled(False) 126 | self.realInsValue.setEnabled(False) 127 | self.delButton.setEnabled(False) 128 | self.saveButton.setEnabled(False) 129 | else: 130 | ins = self.pseudoList.currentItem().text() 131 | 132 | self.pseudoNameValue.setText(ins) 133 | self.operandNumberValue.setText(str(pseudo.pseudo_dict[ins][0])) 134 | self.realInsValue.setPlainText('\n'.join(pseudo.pseudo_dict[ins][1].split(';'))) 135 | 136 | self.pseudoNameValue.setEnabled(True) 137 | self.operandNumberValue.setEnabled(True) 138 | self.realInsValue.setEnabled(True) 139 | self.delButton.setEnabled(True) 140 | self.saveButton.setEnabled(True) 141 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/reg_browser.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QSize 2 | from PyQt5.QtGui import QFont 3 | from PyQt5.QtWidgets import QTextBrowser 4 | 5 | from com.tsreaper.yame.constant.register_list import * 6 | 7 | # ------------------------------------------------------------------------------ 8 | # Register value browser 9 | # ------------------------------------------------------------------------------ 10 | class RegBrowser(QTextBrowser): 11 | 12 | def __init__(self, parent = None): 13 | super(RegBrowser, self).__init__(parent) 14 | 15 | # Set layout 16 | font = QFont('Consolas', 11) 17 | font.setFixedPitch(True) 18 | 19 | self.setFont(font) 20 | self.setStyleSheet( 21 | 'background-color: rgb(41, 49, 52);' \ 22 | 'color: rgb(224, 226, 228);' 23 | ) 24 | 25 | # -------------------------------------------------------------------------- 26 | # Set size of the widget 27 | # -------------------------------------------------------------------------- 28 | def sizeHint(self): 29 | return QSize(220, 0) 30 | 31 | # -------------------------------------------------------------------------- 32 | # Display register value 33 | # -------------------------------------------------------------------------- 34 | def showReg(self, reg): 35 | # Set style 36 | html = \ 37 | '' \ 41 | '' 42 | 43 | reg_list = list(map(lambda r: r[1:], REGISTER_LIST)) + ['pc', 'hi', 'lo'] 44 | 45 | for i in range(0, len(reg_list)): 46 | if i == 32: 47 | html += '' 48 | 49 | html += \ 50 | '' \ 51 | '' \ 52 | '' \ 53 | '' \ 54 | '' \ 55 | '' \ 56 | '' \ 57 | '' \ 58 | % (reg_list[i], reg[i]>>24, (reg[i]>>16) & 0xFF, (reg[i]>>8) & 0xFF, reg[i] & 0xFF) 59 | 60 | html += '
%s %02x%02x%02x%02x
' 61 | 62 | self.setHtml(html) 63 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/setting_dialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QPushButton, QLabel, QLineEdit, QGridLayout, QMessageBox 2 | 3 | import com.tsreaper.yame.constant.config as config 4 | 5 | class SettingDialog(QDialog): 6 | 7 | def __init__(self, parent = None): 8 | super(SettingDialog, self).__init__(parent) 9 | self.initDialog() 10 | 11 | def initDialog(self): 12 | self.memSizeLabel = QLabel('内存大小(字节)') 13 | self.memSizeValue = QLineEdit() 14 | self.textLowerLabel = QLabel('代码段下界(字节)') 15 | self.textLowerValue = QLineEdit() 16 | self.textUpperLabel = QLabel('代码段上界(字节)') 17 | self.textUpperValue = QLineEdit() 18 | self.dataLowerLabel = QLabel('数据段下界(字节)') 19 | self.dataLowerValue = QLineEdit() 20 | self.dataUpperLabel = QLabel('数据段上界(字节)') 21 | self.dataUpperValue = QLineEdit() 22 | 23 | self.memSizeValue.setText(str(config.MEM_SIZE)) 24 | self.textLowerValue.setText(str(config.TEXT_ADDR_LOWER)) 25 | self.textUpperValue.setText(str(config.TEXT_ADDR_UPPER)) 26 | self.dataLowerValue.setText(str(config.DATA_ADDR_LOWER)) 27 | self.dataUpperValue.setText(str(config.DATA_ADDR_UPPER)) 28 | 29 | self.okButton = QPushButton('确认') 30 | self.cancelButton = QPushButton('取消') 31 | self.okButton.clicked.connect(self.okClicked) 32 | self.cancelButton.clicked.connect(self.reject) 33 | 34 | layout = QGridLayout() 35 | layout.addWidget(self.memSizeLabel, 0, 0) 36 | layout.addWidget(self.memSizeValue, 1, 0) 37 | layout.addWidget(self.textLowerLabel, 2, 0) 38 | layout.addWidget(self.textLowerValue, 3, 0) 39 | layout.addWidget(self.textUpperLabel, 2, 1) 40 | layout.addWidget(self.textUpperValue, 3, 1) 41 | layout.addWidget(self.dataLowerLabel, 4, 0) 42 | layout.addWidget(self.dataLowerValue, 5, 0) 43 | layout.addWidget(self.dataUpperLabel, 4, 1) 44 | layout.addWidget(self.dataUpperValue, 5, 1) 45 | layout.addWidget(self.okButton, 6, 0) 46 | layout.addWidget(self.cancelButton, 6, 1) 47 | self.setLayout(layout) 48 | 49 | self.setWindowTitle('汇编器设置') 50 | 51 | def okClicked(self): 52 | try: 53 | mem_size = int(self.memSizeValue.text()) 54 | text_addr_lower = int(self.textLowerValue.text()) 55 | text_addr_upper = int(self.textUpperValue.text()) 56 | data_addr_lower = int(self.dataLowerValue.text()) 57 | data_addr_upper = int(self.dataUpperValue.text()) 58 | except: 59 | QMessageBox.critical(self, '错误', '

只能输入整数!

') 60 | return 61 | 62 | if mem_size > config.MAX_MEM_SIZE: 63 | QMessageBox.critical(self, '错误', '

内存大小超限,至多 1024KB!

') 64 | return 65 | 66 | if text_addr_lower >= 0 and text_addr_lower < mem_size and \ 67 | text_addr_upper >= 0 and text_addr_upper < mem_size and \ 68 | data_addr_lower >= 0 and data_addr_lower < mem_size and \ 69 | data_addr_upper >= 0 and data_addr_upper < mem_size and \ 70 | text_addr_lower <= text_addr_upper and data_addr_lower <= data_addr_upper and \ 71 | (text_addr_upper < data_addr_lower or data_addr_upper < text_addr_lower): 72 | config.MEM_SIZE = mem_size 73 | config.TEXT_ADDR_LOWER = text_addr_lower 74 | config.TEXT_ADDR_UPPER = text_addr_upper 75 | config.DATA_ADDR_LOWER = data_addr_lower 76 | config.DATA_ADDR_UPPER = data_addr_upper 77 | self.accept() 78 | else: 79 | QMessageBox.critical(self, '错误', '

填入数据非法!请重新检查。

') 80 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/simulator_window.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt, QSize 2 | from PyQt5.QtGui import QKeySequence, QIcon, QTextCursor 3 | from PyQt5.QtWidgets import QWidget, QMainWindow, QAction, QDockWidget, QVBoxLayout 4 | from com.tsreaper.yame.ui.message_browser import MessageBrowser 5 | from com.tsreaper.yame.ui.reg_browser import RegBrowser 6 | from com.tsreaper.yame.ui.bin_browser import BinBrowser 7 | from com.tsreaper.yame.ui.terminal_browser import TerminalBrowser 8 | from com.tsreaper.yame.ui.terminal_input import TerminalInput 9 | 10 | import com.tsreaper.yame.simulate.simulator as simulator 11 | from com.tsreaper.yame.simulate.simulation_info_exception import SimulationInfoException 12 | import com.tsreaper.yame.constant.config as config 13 | 14 | def parseHtml(s): 15 | s = s.replace('&', '&') 16 | s = s.replace(' ', ' ') 17 | s = s.replace('<', '<') 18 | s = s.replace('>', '>') 19 | s = s.replace('"', '"') 20 | s = s.replace('\'', ''') 21 | s = s.replace('\n', '
') 22 | return s 23 | 24 | class SimulatorWindow(QMainWindow): 25 | 26 | def __init__(self, parent = None): 27 | super(SimulatorWindow, self).__init__(parent) 28 | 29 | self.initWindow() 30 | 31 | def initWindow(self): 32 | self.setupToolBar() 33 | self.setupDockable() 34 | self.setupTerminal() 35 | self.setWindowTitle('模拟') 36 | 37 | def setupToolBar(self): 38 | self.toolBar = self.addToolBar('Control') 39 | self.toolBar.setIconSize(QSize(48, 48)) 40 | 41 | self.resetAction = QAction( 42 | QIcon('icons/reset.png'), '初始化(F9)', self, 43 | shortcut = QKeySequence('F9'), statusTip = '初始化(F9)', 44 | triggered = self.resetSimulator 45 | ) 46 | self.toolBar.addAction(self.resetAction) 47 | 48 | self.stepAction = QAction( 49 | QIcon('icons/step.png'), '单步(F10)', self, 50 | shortcut = QKeySequence('F10'), statusTip = '单步(F10)', 51 | triggered = self.stepSimulator 52 | ) 53 | self.toolBar.addAction(self.stepAction) 54 | 55 | self.runAction = QAction( 56 | QIcon('icons/run.png'), '运行(F11)', self, 57 | shortcut = QKeySequence('F11'), statusTip = '运行(F11)', 58 | triggered = self.runSimulator 59 | ) 60 | self.toolBar.addAction(self.runAction) 61 | 62 | def setupDockable(self): 63 | self.msgBrowser = MessageBrowser() 64 | self.bottomDock = QDockWidget('信息', self) 65 | self.bottomDock.setFeatures(QDockWidget.DockWidgetClosable) 66 | self.bottomDock.setWidget(self.msgBrowser) 67 | self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock) 68 | self.bottomDock.hide() 69 | 70 | self.regBrowser = RegBrowser() 71 | self.leftDock = QDockWidget('寄存器', self) 72 | self.leftDock.setFeatures(QDockWidget.DockWidgetClosable) 73 | self.leftDock.setWidget(self.regBrowser) 74 | self.addDockWidget(Qt.LeftDockWidgetArea, self.leftDock) 75 | 76 | self.binBrowser = BinBrowser() 77 | self.rightDock = QDockWidget('内存', self) 78 | self.rightDock.setFeatures(QDockWidget.DockWidgetClosable) 79 | self.rightDock.setWidget(self.binBrowser) 80 | self.addDockWidget(Qt.RightDockWidgetArea, self.rightDock) 81 | 82 | def setupTerminal(self): 83 | self.terminal = TerminalBrowser() 84 | self.lineInput = TerminalInput() 85 | self.lineInput.returnPressed.connect(self.updateInput) 86 | 87 | layout = QVBoxLayout() 88 | layout.addWidget(self.terminal) 89 | layout.addWidget(self.lineInput) 90 | central = QWidget() 91 | central.setLayout(layout) 92 | self.setCentralWidget(central) 93 | 94 | def updateBrowser(self): 95 | self.regBrowser.showReg(simulator.reg + [simulator.pc, simulator.hi, simulator.lo]) 96 | self.binBrowser.showBin(simulator.mem, simulator.pc) 97 | 98 | self.terminal.insertHtml(parseHtml(simulator.get_output())) 99 | self.terminal.moveCursor(QTextCursor.End) 100 | 101 | def updateInput(self): 102 | simulator.input_data.extend(self.lineInput.text().split()) 103 | 104 | self.terminal.insertHtml('' + parseHtml(self.lineInput.text()) + '') 105 | self.terminal.moveCursor(QTextCursor.End) 106 | self.lineInput.clear() 107 | 108 | if self.state == 'STEP': 109 | self.stepSimulator() 110 | elif self.state == 'RUN': 111 | self.runSimulator() 112 | 113 | def setWidgetEnabled(self): 114 | self.stepAction.setEnabled(not simulator.program_end and self.state == 'IDLE') 115 | self.runAction.setEnabled(not simulator.program_end and self.state == 'IDLE') 116 | 117 | if self.state != 'IDLE': 118 | self.lineInput.setEnabled(True) 119 | self.lineInput.setFocus() 120 | else: 121 | self.lineInput.setEnabled(False) 122 | 123 | def loadSimulator(self, mem, init_pc): 124 | if init_pc < 0: 125 | simulator.load(mem, config.TEXT_ADDR_LOWER) 126 | self.resetSimulator() 127 | self.bottomDock.show() 128 | self.msgBrowser.append('[WARNING] Can not find "main" label. Program counter will be set to the lower bound of text segment instead.') 129 | else: 130 | simulator.load(mem, init_pc) 131 | self.resetSimulator() 132 | 133 | def resetSimulator(self): 134 | simulator.reset() 135 | self.state = 'IDLE' 136 | 137 | self.bottomDock.hide() 138 | self.terminal.clear() 139 | self.lineInput.clear() 140 | self.msgBrowser.clear() 141 | 142 | self.updateBrowser() 143 | self.setWidgetEnabled() 144 | 145 | def stepSimulator(self): 146 | self.state = 'STEP' 147 | 148 | self.msgBrowser.clear() 149 | self.bottomDock.show() 150 | self.msgBrowser.append('Simulating...') 151 | 152 | try: 153 | simulator.step() 154 | except SimulationInfoException as e: 155 | self.msgBrowser.append('[INFO] %s' % (str(e))) 156 | except Exception as e: 157 | self.msgBrowser.append('[EXCEPTION] %s' % (str(e))) 158 | self.msgBrowser.append('Simulation terminated.') 159 | self.state = 'IDLE' 160 | else: 161 | self.msgBrowser.append('Simulation terminated.') 162 | self.state = 'IDLE' 163 | 164 | self.updateBrowser() 165 | self.setWidgetEnabled() 166 | 167 | def runSimulator(self): 168 | self.state = 'RUN' 169 | 170 | self.msgBrowser.clear() 171 | self.bottomDock.show() 172 | self.msgBrowser.append('Simulating...') 173 | 174 | try: 175 | simulator.run() 176 | except SimulationInfoException as e: 177 | self.msgBrowser.append('[INFO] %s' % (str(e))) 178 | except Exception as e: 179 | self.msgBrowser.append('[EXCEPTION] %s' % (str(e))) 180 | self.msgBrowser.append('Simulation terminated.') 181 | self.state = 'IDLE' 182 | else: 183 | self.msgBrowser.append('Simulation terminated.') 184 | self.state = 'IDLE' 185 | 186 | self.updateBrowser() 187 | self.setWidgetEnabled() 188 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/terminal_browser.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QSize 2 | from PyQt5.QtGui import QFont 3 | from PyQt5.QtWidgets import QTextBrowser 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Terminal of the simulator 7 | # ------------------------------------------------------------------------------ 8 | class TerminalBrowser(QTextBrowser): 9 | 10 | def __init__(self, parent = None): 11 | super(TerminalBrowser, self).__init__(parent) 12 | 13 | # Set layout 14 | font = QFont('Consolas', 11) 15 | font.setFixedPitch(True) 16 | 17 | self.setFont(font) 18 | self.setStyleSheet( 19 | 'background-color: rgb(41, 49, 52);' \ 20 | 'color: rgb(224, 226, 228);' 21 | ) 22 | 23 | # -------------------------------------------------------------------------- 24 | # Set size of the widget 25 | # -------------------------------------------------------------------------- 26 | def sizeHint(self): 27 | return QSize(480, 480) 28 | -------------------------------------------------------------------------------- /src/com/tsreaper/yame/ui/terminal_input.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QSize 2 | from PyQt5.QtGui import QFont 3 | from PyQt5.QtWidgets import QLineEdit 4 | 5 | # ------------------------------------------------------------------------------ 6 | # Line input for the terminal of the simulator 7 | # ------------------------------------------------------------------------------ 8 | class TerminalInput(QLineEdit): 9 | 10 | def __init__(self, parent = None): 11 | super(TerminalInput, self).__init__(parent) 12 | 13 | # Set layout 14 | font = QFont('Consolas', 11) 15 | font.setFixedPitch(True) 16 | 17 | self.setFont(font) 18 | self.setStyleSheet( 19 | 'background-color: rgb(41, 49, 52);' \ 20 | 'color: rgb(224, 226, 228);' 21 | ) 22 | 23 | # -------------------------------------------------------------------------- 24 | # Set size of the widget 25 | # -------------------------------------------------------------------------- 26 | def sizeHint(self): 27 | return QSize(480, 0) 28 | -------------------------------------------------------------------------------- /src/config.cfg: -------------------------------------------------------------------------------- 1 | [blti] 2 | operand = 3 3 | real = lui $at, ([2])>>16;ori $at, $at, ([2])&0xFFFF;subu $at, [1], $at;bltz $at, [3] 4 | 5 | [blt] 6 | operand = 3 7 | real = subu $at, [1], [2];bltz $at, [3] 8 | 9 | [nop] 10 | operand = 0 11 | real = sll $zero, $zero, 0 12 | 13 | [not] 14 | operand = 1 15 | real = addi $at, $zero, -1;xor [1], [1], $at 16 | 17 | [beqi] 18 | operand = 3 19 | real = lui $at, ([2])>>16;ori $at, $at, ([2])&0xFFFF;beq [1], $at, [3] 20 | 21 | [muli] 22 | operand = 3 23 | real = lui $at, ([3])>>16;ori $at, $at, ([3])&0xFFFF;mult $at, [2];mflo [1] 24 | 25 | [seq] 26 | operand = 3 27 | real = subu $at, [2], [3];sltu [1], $zero, $at;addi $at, $zero, 1;subu [1], $at, [1] 28 | 29 | [li] 30 | operand = 2 31 | real = lui [1], ([2])>>16;ori [1], [1], ([2])&0xFFFF 32 | 33 | [beqz] 34 | operand = 2 35 | real = beq [1], $zero, [2] 36 | 37 | [bnei] 38 | operand = 3 39 | real = lui $at, ([2])>>16;ori $at, $at, ([2])&0xFFFF;bne [1], $at, [3] 40 | 41 | [mul] 42 | operand = 3 43 | real = mult [2], [3];mflo [1] 44 | 45 | [bgt] 46 | operand = 3 47 | real = subu $at, [1], [2];bgtz $at, [3] 48 | 49 | [la] 50 | operand = 2 51 | real = lui $at, (@[2]@)>>16;ori $at, $at, (@[2]@)&0xFFFF;add [1], $zero, $at 52 | 53 | [move] 54 | operand = 2 55 | real = add [1], $zero, [2] 56 | 57 | [b] 58 | operand = 1 59 | real = j [1] 60 | 61 | [bgti] 62 | operand = 3 63 | real = lui $at, ([2])>>16;ori $at, $at, ([2])&0xFFFF;subu $at, $at, [1];bltz $at, [3] 64 | 65 | [bge] 66 | operand = 3 67 | real = subu $at, [1], [2];bgez $at, [3] 68 | 69 | [`] 70 | mem_size = 8192 71 | text_addr_lower = 0 72 | text_addr_upper = 4095 73 | data_addr_lower = 4096 74 | data_addr_upper = 8191 75 | 76 | -------------------------------------------------------------------------------- /src/icons/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/src/icons/reset.png -------------------------------------------------------------------------------- /src/icons/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/src/icons/run.png -------------------------------------------------------------------------------- /src/icons/step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/src/icons/step.png -------------------------------------------------------------------------------- /src/icons/yame.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/src/icons/yame.ico -------------------------------------------------------------------------------- /src/icons/yame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsreaper/yame-a-mips-editor/c1e306054317711e2dbf263a4b9f8426effcfa00/src/icons/yame.png -------------------------------------------------------------------------------- /src/main.pyw: -------------------------------------------------------------------------------- 1 | import sys 2 | from PyQt5.QtWidgets import QApplication 3 | from com.tsreaper.yame.ui.main_window import MainWindow 4 | 5 | app = QApplication(sys.argv) 6 | window = MainWindow() 7 | window.resize(960, 640) 8 | window.show() 9 | sys.exit(app.exec_()) 10 | -------------------------------------------------------------------------------- /test/guess.asm: -------------------------------------------------------------------------------- 1 | # This program generates a random digit number with unique digits given a 2 | # length value input by the user (sized values of 1 to 9), and asks the user 3 | # to try to guess the generated number. For every digit the guess shares with the generated 4 | # number, the program prints the word Pico, but if the digit is in the same locations, it prints 5 | # Fermi. If no digits are shared, the program prints Bagel. If the user gives up they can input 6 | # 0 and the program will show them the generated value, along with the number of times 7 | # the user guessed incorrently. 8 | # By Alex Plaza 9 | 10 | .data 11 | 12 | init1: .asciiz "\nThe computer has generated a " 13 | init2: .asciiz " digit random number with unique digits. Try to guess it, or input 0 to give up." 14 | getLength: .asciiz "Select length of target b/w 1-9: " 15 | iPrompt: .asciiz "\nPlease enter your guess: " 16 | Fermi: .asciiz "Fermis" 17 | Pico: .asciiz "Pico" 18 | Bagel: .asciiz "Bagel" 19 | win: .asciiz "\nYou Won!!" 20 | newLine: .asciiz "\n" 21 | targetp: .asciiz "\nTarget Was: " 22 | counter: .asciiz "Guesses: " 23 | 24 | targetArray: .word 0,0,0,0,0,0,0,0,0 25 | guessArray: .word 0,0,0,0,0,0,0,0,0 26 | guessCounter: .word 0 27 | 28 | .text 29 | 30 | main: 31 | la $s5, guessCounter # s5 = guessCounter: counts the number of guesses 32 | lw $s5, 0($s5) 33 | 34 | la $a0, getLength 35 | li $v0, 4 36 | syscall # print message to get length from user 37 | 38 | li $v0, 5 39 | syscall 40 | move $t9, $v0 # move user input into t9 41 | 42 | la $a0, targetArray # a0 = address of the targetArray 43 | move $a1, $t9 # a1 = length 44 | jal GenerateNumber # GenerateNumber(targetArray, length) 45 | move $s7, $v0 # s7 = TargetNumber (NOT THE ARRAY, BUT THE ACTUAL NUMBER) 46 | 47 | la $a0, init1 # print initial message 48 | li $v0, 4 49 | syscall 50 | 51 | move $a0, $t9 # print length 52 | li $v0, 1 53 | syscall 54 | 55 | la $a0, init2 56 | li $v0, 4 57 | syscall 58 | 59 | # Uncomment the code below to "cheat" and 60 | # see the generated number before start guessing 61 | 62 | # move $a0, $t9 63 | # la $a1, targetArray 64 | # jal printList 65 | 66 | # la $a0, newLine 67 | # li $v0, 4 68 | # syscall 69 | 70 | game: 71 | move $a0, $s7 72 | la $a1, guessArray 73 | move $a2, $t9 74 | jal getInput #getInput(generatedNumber, addressGuessArray, length) 75 | 76 | la $a0, targetArray 77 | la $a1, guessArray 78 | move $a2, $t9 79 | jal CompareArrays #CompareArrays(addressTargetArray, addressGuessArray, length) 80 | 81 | j game 82 | 83 | #-------------------- GenerateNumber --------------------# 84 | # The function GenerateNumber takes as a parameter 85 | # the array where the generated number will be stored. 86 | # 87 | # It generates a random number b/t 10*length*1 88 | # and 10*length*9 using syscall 42 (i.e. if length = 4 89 | # then the generated number is between 1000-9990). 90 | # 91 | # Then, it separates the digits in the generated number 92 | # and stores them in the array that was passed as a 93 | # parameter. 94 | # 95 | # Then it checks if the digits of the number are unique, 96 | # if they are not, it generates a random number again. 97 | # 98 | # At the end the function will return the generated number, 99 | # and the digits of generated number will be in the array passed. 100 | 101 | GenerateNumber: 102 | ######### Allocate Stack Space ######### 103 | addi $sp, $sp, -12 # allocate stack space for 3 values 104 | sw $ra, 0($sp) 105 | sw $s3, 4($sp) 106 | sw $s4, 8($sp) 107 | 108 | ######### Init variables ######### 109 | move $s3, $a0 # s3 = array address 110 | move $s4, $a1 # s4 = length or array 111 | move $t8, $0 # t8-t1 auxiliary variables used for intermidiate calculations. 112 | move $t7, $0 113 | move $t5, $0 114 | move $t4, $0 115 | move $t3, $0 116 | move $t1, $0 117 | 118 | GenerateRandom: 119 | ######### Generate Random Number ######### 120 | move $t0, $s4 # t0 = i 121 | li $t7, 1 # lower bound for random 122 | li $a1, 8 # upper bound for random 123 | 124 | CreateLimit: 125 | beqi $t0, 1, generate # if i != 1 126 | 127 | muli $a1, $a1, 10 # mul lupper bound by 10 128 | muli $t7, $t7, 10 # mul lowe bound by 10 129 | addi $t0, $t0, -1 # i-- 130 | 131 | j CreateLimit 132 | 133 | generate: 134 | li $v0, 42 # generates random number and places it in a0 135 | syscall 136 | add $t8, $a0, $t7 # t8 = target number 137 | 138 | move $a0, $t8 139 | move $a1, $s3 140 | move $a2, $s4 141 | jal SeparateNumbers # SeparateNumbers(generatedNumber, addressTargetArray, length) 142 | 143 | move $t6, $0 # i = 0 144 | move $t2, $0 145 | addi $t2, $t2, 1 # j = 1 146 | 147 | UniqueLoop: # check if generated number has unique digits 148 | bge $t6, $s4, EndUnique # if i < length 149 | 150 | sll $t5, $t6, 2 151 | add $t4, $s3, $t5 152 | lw $t3, 0($t4) # t3 = array[i] 153 | 154 | InnerUnique: 155 | bge $t2, $s4, EndInnerUnique # if j < length 156 | 157 | sll $t5, $t2, 2 158 | add $t4, $s3, $t5 159 | lw $t1, 0($t4) # t1 = array[j] 160 | 161 | beq $t1, $t3, GenerateRandom # if array[i] = array[j] 162 | addi $t2, $t2, 1 163 | 164 | j InnerUnique 165 | 166 | EndInnerUnique: 167 | addi $t6, $t6, 1 # i++ 168 | addi $t2, $t6, 1 # j = i+1 169 | j UniqueLoop 170 | 171 | EndUnique: 172 | ######### Prepare Return Values ######### 173 | move $v0, $t8 # move generated number to retun register 174 | ######### Restore Stack Space ######### 175 | lw $ra, 0($sp) 176 | lw $s3, 4($sp) 177 | lw $s4, 8($sp) 178 | addi $sp, $sp, 12 179 | jr $ra 180 | 181 | #-------------------- SeparateNumber --------------------# 182 | # The SeparateNumbers function takes as parameters a number, 183 | # an array address, and the length of the array. 184 | # 185 | # The function separates the digits of the number using 186 | # modulos and divisions, and stores the digits in the 187 | # provided array. 188 | 189 | SeparateNumbers: 190 | ######### Allocate Stack Space ######### 191 | addi $sp, $sp, -16 # allocate stack space for 4 values 192 | sw $ra, 0($sp) 193 | sw $s0, 4($sp) 194 | sw $s1, 8($sp) 195 | sw $s2, 12($sp) 196 | ######### Init variables ######### 197 | move $s0, $a0 # s0 = number to split 198 | move $s1, $a1 # s1 = address of array 199 | move $s2, $a2 # s2 = length 200 | li $t0, 1 # t0-t4 auxiliary variables used for intermidiate calculations. 201 | li $t1, 10 202 | move $t2, $0 203 | move $t3, $0 204 | move $t4, $0 205 | move $t5, $s2 206 | addi $t5, $t5, -1 # t5 = index = length - 1 207 | 208 | SeparateLoop: 209 | ######### Separate Numbers ######### 210 | bltz $t5, EndSeparate # if index >= 0 211 | 212 | div $s0, $t1 213 | mfhi $t2 # t2 = s0 % t1 214 | div $t2, $t0 215 | mflo $t3 # t3 = t2/t0 (integer division) --> ith digit 216 | 217 | sll $t4, $t5, 2 218 | add $t6, $s1, $t4 # t6: get offset for array[index*4] 219 | sw $t3, 0($t6) # Store digit t3 in array[index*4] 220 | addi $t5, $t5, -1 # i-- 221 | 222 | muli $t0, $t0, 10 223 | muli $t1, $t1, 10 224 | 225 | j SeparateLoop 226 | 227 | EndSeparate: 228 | ######### Restore Stack Space ######### 229 | lw $ra, 0($sp) 230 | lw $s0, 4($sp) 231 | lw $s1, 8($sp) 232 | lw $s2, 12($sp) 233 | addi $sp, $sp, 16 234 | jr $ra 235 | 236 | #-------------------- getInput --------------------# 237 | # getInput takes as a a parameter the target number 238 | # and the address of the guessArray. 239 | # 240 | # It prompts the user to input a guess, then it 241 | # increases the guess counter, then if the target is 242 | # equal to the guess, go to Win, and if the target is 243 | # the special input 0 go to Terminate. 244 | # 245 | # Otherwise, separate the digits of the guess number 246 | # and store the digits in the provided array. 247 | 248 | getInput: 249 | ######### Allocate Stack Space ######### 250 | addi $sp, $sp, -16 # allocate stack space for 3 values 251 | sw $ra, 0($sp) 252 | sw $s7, 4($sp) 253 | sw $s6, 8($sp) 254 | sw $s4, 12($sp) 255 | ######### Init variables ######### 256 | move $s4, $a2 # s4 = length 257 | move $s6, $a1 # s6 = guessArray 258 | move $s7, $a0 # s7 = target number 259 | move $t5, $0 # t5-t6 = auxiliary variables used for intermidiate operations 260 | move $t6, $0 261 | ######### Print prompt and get guess from user ######### 262 | la $a0, iPrompt 263 | li $v0, 4 264 | syscall # print please input guess 265 | 266 | li $v0, 5 267 | syscall 268 | move $t6, $v0 # t6 = guess 269 | 270 | addi $s5, $s5, 1 # guessCounter++ 271 | 272 | beq $t6, $s7, Win # if the targetNumber and guessNumber are equal go to Win 273 | beq $t6, $zero, Terminate # if input is 0 go to Terminate 274 | j SeparateGuess # if none of the beq above are true, 275 | # continue to separate the digits in guess 276 | 277 | Win: 278 | ######### Print Win Message ######### 279 | la $a0, win 280 | li $v0, 4 281 | syscall # print you Won!! 282 | 283 | la $a0, newLine # print new line 284 | li $v0, 4 285 | syscall 286 | 287 | la $a0, counter # guesses: 288 | li $v0, 4 289 | syscall 290 | 291 | move $a0, $s5 # print guessCounter 292 | li $v0, 1 293 | syscall 294 | 295 | li $v0, 10 296 | syscall 297 | 298 | Terminate: 299 | ######### Print Terminate Message ######### 300 | addi $s5, $s5, -1 301 | 302 | la $a0, targetp # print target was: 303 | li $v0, 4 304 | syscall 305 | 306 | move $a0, $s4 307 | la $a1, targetArray 308 | jal printList # printList(length, addressTargetArray) 309 | 310 | la $a0, newLine # print new line 311 | li $v0, 4 312 | syscall 313 | 314 | la $a0, counter # guesses: 315 | li $v0, 4 316 | syscall 317 | 318 | move $a0, $s5 # print guessCounter 319 | li $v0, 1 320 | syscall 321 | 322 | li $v0, 10 323 | syscall 324 | 325 | SeparateGuess: 326 | ######### Separate digits in guess ######### 327 | move $a0, $t6 328 | move $a1, $s6 329 | move $a2, $s4 330 | jal SeparateNumbers # SeparateNumbers(guessedNumber, guessArray, length) 331 | 332 | ######### Restore Memory ######### 333 | lw $ra, 0($sp) 334 | lw $s7, 4($sp) 335 | lw $s6, 8($sp) 336 | lw $s4, 12($sp) 337 | addi $sp, $sp, 16 338 | jr $ra 339 | 340 | #-------------------- CompareArrays --------------------# 341 | # The function ComapareArrays takes as parametes two arrays, 342 | # and the length of the arrays (it assumes the leght of both 343 | # arrays is equivalent). It compares the two arrays using 344 | # two loops. 345 | # 346 | # Each number of array1 is compared with each number of array2. 347 | # 348 | # If, any of the number are the same, generate a message 349 | # accordingly. 350 | # 351 | # If the comparison is over and no picos or fermis were 352 | # printed, print bagel. 353 | 354 | CompareArrays: 355 | ######### Allocate Stack Space ######### 356 | addi $sp, $sp, -12 # allocate stack space for 3 values 357 | sw $ra, 0($sp) 358 | sw $s0, 4($sp) 359 | sw $s1, 8($sp) 360 | ######### init Variables ######### 361 | move $s0, $a0 # s0 = array1 362 | move $s1, $a1 # s1 = array2 363 | move $s2, $a2 # s2 = lenght 364 | move $t3, $0 # t3 = Fermis and Pico Counter 365 | move $t4, $0 # t4 = aux = 0 366 | move $t6, $0 # t6 = aux = 0 367 | move $t7, $0 # t7 = i = 0 368 | move $t5, $0 # t5 = j = 0 369 | 370 | CompareLoop: 371 | ######### Compare Numbers ######### 372 | bge $t7, $s2, EndCompare # if i < length 373 | 374 | sll $t6, $t7, 2 375 | add $t4, $s0, $t6 376 | lw $t0, 0($t4) # t0 = array1[i] 377 | 378 | Inner: 379 | bge $t5, $s2, EndInner # if j < length 380 | 381 | sll $t6, $t5, 2 382 | add $t4, $s1, $t6 383 | lw $t1, 0($t4) # t1 = array2[i] 384 | 385 | beq $t0, $t1, generateMessage # if array[i] = array[j] generate a message. 386 | addi $t5, $t5, 1 387 | 388 | j Inner 389 | 390 | generateMessage: 391 | ######### Generate Message ######### 392 | addi $t3, $t3, 1 # Fermis&PicoCounter++ 393 | beq $t7, $t5, printFermi # if i = j 394 | 395 | ######### Print Pico ######### 396 | la $a0, newLine 397 | li $v0, 4 398 | syscall 399 | 400 | la $a0, Pico 401 | li $v0, 4 402 | syscall 403 | addi $t5, $t5, 1 404 | 405 | j Inner 406 | 407 | printFermi: 408 | ######### Print Fermi ######### 409 | la $a0, newLine 410 | li $v0, 4 411 | syscall 412 | 413 | la $a0, Fermi 414 | li $v0, 4 415 | syscall 416 | addi $t5, $t5, 1 417 | 418 | j Inner 419 | 420 | printBagel: 421 | ######### Print Bagel ######### 422 | la $a0, newLine 423 | li $v0, 4 424 | syscall 425 | 426 | la $a0, Bagel 427 | li $v0, 4 428 | syscall 429 | 430 | j afterBagel 431 | 432 | EndInner: 433 | ######### End Inner loop and Restart outer loop ######### 434 | move $t5, $0 # restart inner loop j = 0 435 | addi $t7, $t7, 1 # j++ 436 | j CompareLoop 437 | 438 | EndCompare: 439 | ######### Print Bagel if necessary, and end function ######### 440 | beqz $t3, printBagel # if no picos or fermis were printer, print bagel 441 | 442 | afterBagel: 443 | ######### Restore Stack Space ######### 444 | lw $ra, 0($sp) 445 | lw $s0, 4($sp) 446 | lw $s1, 8($sp) 447 | addi $sp, $sp, 12 448 | jr $ra 449 | 450 | #-------------------- printList --------------------# 451 | # Function printList take as parameters the length of 452 | # the array and its address and loops over the elements 453 | # and prints them one by one. 454 | # 455 | # No need to allocate stack space since this function 456 | # isn't using s register. 457 | 458 | printList: 459 | move $t0, $0 # t0: index i = 0 460 | move $t1, $a0 # t1: arryLength 461 | move $t2, $a1 # t2: Address 462 | 463 | printLoop: 464 | bge $t0, $t1, endPrint # if i < arrayLength 465 | 466 | lw $a0, 0($t2) # print element 467 | li $v0, 1 468 | syscall 469 | 470 | addi $t0, $t0, 1 # ++i 471 | addi $t2, $t2, 4 # Address = address + 4 472 | j printLoop 473 | 474 | endPrint: 475 | jr $ra 476 | -------------------------------------------------------------------------------- /test/sum.asm: -------------------------------------------------------------------------------- 1 | .text 2 | main: la $1, data # address of data[0] 3 | addi $4, $1, 80 # address of data[0] 4 | call: addi $5, $0, 4 # counter 5 | jal sum # call function 6 | return: sw $2, 0($4) # store result 7 | lw $9, 0($4) # check sw 8 | sw $9, 4($4) # store result again 9 | addi $8, $9, -96 # $8 <- $9 - 96 10 | 11 | print: li $2, 1 # print $8 12 | add $4, $8, $0 # print $8 13 | syscall # print $8 (should be 0x1f8 = 504) 14 | li $2, 11 # print space 15 | li $4, 32 # print space 16 | syscall # print space 17 | li $2, 1 # print $9 18 | add $4, $9, $0 # print $9 19 | syscall # print $9 (should be 0x258 = 600) 20 | li $2, 10 # exit 21 | syscall # exit 22 | 23 | sum: add $8, $0, $0 # sum function entry 24 | loop: lw $9, 0($4) # load data 25 | add $8, $8, $9 # sum 26 | addi $5, $5, -1 # counter - 1 27 | addi $4, $4, 4 # address + 4 28 | slt $3, $0, $5 # finish? 29 | bne $3, $0, loop # finish? 30 | or $2, $8, $0 # move result to $v0 31 | jr $ra # return 32 | nop # done 33 | 34 | .data 35 | data: .word 0xBF800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000A3, 0x00000027, 0x00000079, 0x00000115, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 36 | --------------------------------------------------------------------------------