26 - 汇编编译器

---- 整理自B站UP主 踌躇月光 的视频

1. CPU 电路

请添加图片描述

2. 汇编编译器

将上一节我们手动把 MOV A,5 转换成机器语言的方式,用汇编编译器实现。

# program.asm
MOV A, 5 ; this is annotation

MOV A, 5
MOV B, 10
MOV C, 0x10
MOV D, 20

MOV A, 0x30

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

请添加图片描述

相关推荐

  1. 【RV1126】Ubuntu22.04下sdk编译问题汇集

    2024-04-30 20:30:02       36 阅读
  2. 周报 | 24.4.22-24.4.28文章汇总

    2024-04-30 20:30:02       38 阅读
  3. 预处理、编译汇编、链接过程

    2024-04-30 20:30:02       37 阅读
  4. g++ 预处理 编译 汇编 链接 命令

    2024-04-30 20:30:02       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-30 20:30:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-30 20:30:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-30 20:30:02       87 阅读
  4. Python语言-面向对象

    2024-04-30 20:30:02       96 阅读

热门阅读

  1. 【华为OD机试】-(C卷+D卷)-2024最新真题目录

    2024-04-30 20:30:02       58 阅读
  2. 【个人博客搭建】(13)SqlSugar仓储实现

    2024-04-30 20:30:02       25 阅读
  3. 黑客眼中最简单的漏洞,弱口令暴力破解

    2024-04-30 20:30:02       30 阅读
  4. Spring中实现策略模式的几种方式

    2024-04-30 20:30:02       27 阅读
  5. Kafka集群搭建

    2024-04-30 20:30:02       36 阅读
  6. ndk编译android系统下运行的ffmpeg配置

    2024-04-30 20:30:02       34 阅读
  7. 使用通义千问,为汽车软件需求生成测试用例

    2024-04-30 20:30:02       29 阅读
  8. WebSocket 的封装

    2024-04-30 20:30:02       39 阅读
  9. Android Glide 获取动图的第一帧

    2024-04-30 20:30:02       33 阅读