TL;DR(或许也没有那么长)
食用方法
- 下载Python脚本
replace.py
(或者也许你愿意从文末复制?)
https://bhpan.buaa.edu.cn/link/AA142FF068D230494AB6CF02281EEFE6FF(无需存档)
Name: replace.py Expires: 2023-11-26 21:59 Pickup Code:
0xdeadbeef
1
| python replace.py <.circ文件名> <机器码文本文件名>
|
大功告成!修改后的.circ文件将保存在当前目录中,文件名为原文件名后加上当前的时间,格式为原文件名_机器码文本文件名_hhmmss.circ
使用例:
值得注意的事
支持什么机器码文本文件?
Mars
以Hexadecimal Text
格式导出的文件
Logisim
通过ROM->Contents->Save
方式导出的文件
提示 No match?
鉴于P3课下的需求,本脚本设置为匹配Address Bit Width = 12
且Data Bit Width=32
的ROM,如果有其他需求,可以自行修改pattern
变量的相关内容
背景
向Logisim中导入取自Mars的机器码时,需要手动修改文本文件,且涉及多次鼠标点击。鉴于笔者较懒,遂尝试编写脚本一劳永逸地解决问题。
前期准备
瞪眼法发现,Logisim
以.circ
文件格式保存电路,这些文件本质上是 XML
文档,允许以文本形式进行读取和编辑,样式如下图:
以文本格式打开某含有ROM组件的.circ
文件,搜索ROM
关键词得:
我们先看第一个:
这个显然不是,略过(逃
在此部分实际上存储的是从左侧工具栏选择组件时的默认设置。你可能注意到,当从侧边栏选择一个组件并在将其放置到画布上之前修改了左下角的组件属性,那么下一次选择该组件时,其默认属性就会变为之前修改过的设置,这些设置重启Logisim后仍会保留。这些个默认设置就存储在这里。
再看第二个:
直觉告诉我们它就是我们想要的,我们观察它的几个标签:
loc=(310,170)
决定了组件在画布上的位置,鉴于每个人的画布布局(通常来说)都不同,这部分的影响需要排除
addrWidth=12
和dataWidth=32
规定了组件的地址位宽的数据位宽,在P3中,ROM的要求是地址位宽12位,数据位宽32位
contents
显然我们想要替换的机器码就保存在这里
脚本编写
正则表达式设计
1
| (<comp lib="4" loc="\([^"]+\)" name="ROM">\s*<a name="addrWidth" val="12"/>\s*<a name="dataWidth" val="32"/>\s*<a name="contents">addr/data: 12 32\s*)(.*?)(</a>\s*</comp>)
|
比OOPre的三个式子简单多了()
Python程序设计
略,总之就是读取-查找-替换-输出四步走
改进空间
本脚本有很多改进的方向,笔者本人就想到了两个
- 当前脚本使用的
re.search()
方法只返回第一个匹配结果,对于P3的要求足够,但是可以改进
- 可以接受可选的多个输入,决定匹配ROM的
addrWidth
和dataWidth
源代码
(或者也许你愿意从文末复制?)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import sys import re import datetime
def read_file(file_name): try: with open(file_name, 'r') as f: return f.readlines() except FileNotFoundError: print("File not found: " + file_name) exit(1) def write_file(file_name, lines): try: with open(file_name, 'w') as f: f.writelines(lines) except FileNotFoundError: print("File not found: " + file_name) exit(1)
def main(circ_flie_path, hex_text_path): circ_data = read_file(circ_flie_path) hex_data = read_file(hex_text_path)
new_hex_code = [] start = False for line in hex_data: if line.strip() != 'v2.0 raw': start = True if start: new_hex_code.append(line)
circ_content = "".join(circ_data) pattern = ( r'(<comp lib="4" loc="\([^"]+\)" name="ROM">\s*' r'<a name="addrWidth" val="12"/>\s*' r'<a name="dataWidth" val="32"/>\s*' r'<a name="contents">addr/data: 12 32\s*)(.*?)(</a>\s*</comp>)' ) match = re.search(pattern, circ_content, re.DOTALL) if match is None: print("No match") exit(1) result = []
result.append(circ_content[:match.start()]) result.append(match.group(1)) result.append("".join(new_hex_code)) result.append(match.group(3)) result.append(circ_content[match.end():])
result = "".join(result)
circ_name = circ_flie_path.split('\\')[-1].split('.')[0] hex_name = hex_text_path.split('\\')[-1].split('.')[0] new_file_name = circ_name + "_" + hex_name + "_" + datetime.datetime.now().strftime("%H%M%S") + ".circ" write_file(new_file_name, result) print('Replace success, new file: ' + new_file_name)
if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python replace.py <circ_file_path> <hex_text_path>") exit(1) main(sys.argv[1], sys.argv[2])
|