---- 整理自B站UP主 踌躇月光 的视频
1. CPU 电路
2. 汇编编译器
将上一节我们手动把 MOV A,5
转换成机器语言的方式,用汇编编译器实现。
bash
# program.asm
MOV A, 5 ; this is annotation
MOV A, 5
MOV B, 10
MOV C, 0x10
MOV D, 20
MOV A, 0x30
HLT;
python
# compiler.py
import os
import re # re模块提供了各种各样的正则表达式方法
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
inputfile = os.path.join(dirname, 'program.asm')
outputfile = os.path.join(dirname, 'program.bin')
annotation = re.compile(r"(.*?);.*") # 这个正则表达式的目的是捕获分号;之前的所有内容
codes = []
OP2 = {
'MOV': ASM.MOV
}
OP1 = {
}
OP0 = {
'NOP': ASM.NOP,
'HLT': ASM.HLT,
}
OP2SET = set(OP2.values())
OP1SET = set(OP1.values())
OP0SET = set(OP0.values())
REGISTERS = {
"A": pin.A,
"B": pin.B,
"C": pin.C,
"D": pin.D,
}
class Code(object):
def __init__(self, number, source):
self.numer = number # 行号
self.source = source.upper() # 源代码
self.op = None
self.dst = None
self.src = None
self.prepare_source() # 调用预处理源代码
def get_op(self):
if self.op in OP2:
return OP2[self.op]
if self.op in OP1:
return OP1[self.op]
if self.op in OP0:
return OP0[self.op]
raise SyntaxError(self)
def get_am(self, addr): # 获取目的操作数和源操作数
if not addr:
return 0, 0
if addr in REGISTERS: # 如果是寄存器,返回寄存器编码。示例就是A寄存器
return pin.AM_REG, REGISTERS[addr]
if re.match(r'^[0-9]+$', addr): # 如果是数字,返回立即数。示例就是5
return pin.AM_INS, int (addr)
if re.match(r'^0X[0-9A-F]+$', addr): # 如果是十六进制数,返回十六进制立即数
return pin.AM_INS, int(addr, 16)
raise SyntaxError(self)
def prepare_source(self): # 预处理汇编代码,以MOV A,5举例
tup = self.source.split(',') # 用逗号分隔
if len(tup) > 2:
raise SyntaxError(self)
if len(tup) == 2:
self.src = tup[1].strip() # 5赋值给源操作数
tup = re.split(r" +", tup[0]) # 正则表达式,将tup[0]字符串中的一个或多个连续空格作为分隔符,将字符串拆分成多个部分,并返回一个包含拆分后的所有部分的列表。将 MOV A拆分成了MOV和A
if len(tup) > 2:
raise SyntaxError(self)
if len(tup) == 2:
self.dst = tup[1].strip() # A赋值给了目的操作数
self.op = tup[0].strip() # MOV赋值给指令
def compile_code(self):
# 指令IR ==> op + amd + ams
# MOV ==> 1000 + [aa] + [bb]
op = self.get_op()
amd, dst = self.get_am(self.dst) # 预处理之后已经拿到self.dst=A
ams, src = self.get_am(self.src) # 预处理之后已经拿到self.src=5
if op in OP2SET:
ir = op | (amd << 2) | ams
elif op in OP1SET:
ir = op | amd
else:
ir = op
return [ir, dst, src]
def __repr__(self):
return f'[{self.numer}] - {self.source}'
class SyntaxError(Exception):
def __init__(self,code: Code, *args, **kwargs):
super().__init__(*args, **kwargs)
self.code = code
def compile_program():
with open(inputfile, encoding='utf8') as file: # 打开汇编源码
lines = file.readlines()
for index, line in enumerate(lines):
source = line.strip() # 将两端的空格去掉
if ';' in source: # 将;后面的去掉
match = annotation.match(source) # 使用之前定义的正则表达式annotation来匹配分号之前的内容
source = match.group(1)
if not source: # 检查source是否为空或只包含空白字符(例如空格、制表符、换行符等)
continue
code = Code(index + 1, source) # 传入行号和每行的汇编代码
codes.append(code)
with open(outputfile, 'wb') as file:
for code in codes:
values = code.compile_code()
for value in values:
result = value.to_bytes(1, byteorder='little')
file.write(result)
def main():
compile_program()
# try:
# compile_program()
# except SyntaxError as e:
# print(f'Syntax error at {e.code}')
# return
print('compile program.asm finished!!!')
if __name__ == '__main__':
main()
启动编译,我们可以得到 program.bin,需要将其加载到内存 RAM 中执行。