26 - 汇编编译器

---- 整理自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 中执行。

3. 实验

【26 - 汇编编译器】

相关推荐
lhbweilai8 分钟前
分析FreeRTOS内核对象API的源码实现(八)
c语言·开发语言·stm32·单片机
努力努力再努力wz12 分钟前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节
咩咩啃树皮25 分钟前
JavaScript 中 this 指向为什么总在变?
开发语言·前端·javascript
程序员清风32 分钟前
算法题中的边界条件、测试与调试
开发语言·算法·kotlin
砚底藏山河38 分钟前
python量化实操:冷热分层存储
java·开发语言·python·金融
十五年专注C++开发40 分钟前
pybind11:一个跨C++和Python平台的编程库
开发语言·c++·python·pybind11
CodeYp20191 小时前
第11讲:stack 和 queue
开发语言·c++·queue·stack
炘爚1 小时前
数据结构算法总结
数据结构·python·算法
信誓旦旦的程序猿1 小时前
【Python量化实战宝典 #18】MACD 金叉死叉手册:DIF、DEA、柱体全拆解(Python 可跑)
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
Patrick在香港1 小时前
一个加 1 的动作,把最大值变成了最小值
python·numpy·数据类型·整数溢出·数据精度