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 - 汇编编译器】

相关推荐
_.Switch1 分钟前
Python机器学习模型的部署与维护:版本管理、监控与更新策略
开发语言·人工智能·python·算法·机器学习
醉颜凉4 分钟前
银河麒麟桌面操作系统修改默认Shell为Bash
运维·服务器·开发语言·bash·kylin·国产化·银河麒麟操作系统
NiNg_1_23410 分钟前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
带带老表学爬虫18 分钟前
java数据类型转换和注释
java·开发语言
Hoper.J20 分钟前
PyTorch 模型保存与加载的三种常用方式
人工智能·pytorch·python
qianbo_insist21 分钟前
simple c++ 无锁队列
开发语言·c++
BigYe程普31 分钟前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
彭于晏68933 分钟前
Android广播
android·java·开发语言
弱冠少年1 小时前
websockets库使用(基于Python)
开发语言·python·numpy
长天一色1 小时前
C语言日志类库 zlog 使用指南(第五章 配置文件)
c语言·开发语言