编译器构造:从零手写汇编与反汇编程序(二)

这个秋季,三碗饭会开始更新自己在秋季课程的笔记,主要包括Introduction to ML,可能还会有Complier Construction 以及 Computer Graphics.

本文将介绍Complier Construction中关于模拟器,汇编以及反汇编的知识,在下一章我们会从0写一个简易模拟器,以及汇编,反汇编程序。这门课如果没有兴趣,会非常无聊非常无聊,当然我一开始也觉得挺无聊的,毕竟搞AI Agent比这个有意思多,但我还是会尽量以简单的描述(避免复杂的解释),以及有意思的例子去讲这些知识。

1. 概要

了解下之后我们会完成的三个内容。
汇编器:

将人类可读的汇编代码(.uma)转换为机器码(.um)

相当于"翻译官",把人类语言翻译成机器语言

反汇编器

将机器码(.um)转换回汇编代码(.uma)

相当于"解码器",把机器语言翻译回人类语言

主要用于调试和验证

模拟器:

执行机器码(.um)

相当于"虚拟CPU",实际运行程序

负责程序的实际执行

2. 核心组件

2.1 指令结构

反汇编器使用与汇编器相同的指令结构:

cpp 复制代码
enum class BinIns {
    HALT,   // 停止程序
    IN,     // 输入
    OUT,    // 输出
    ADD,    // 加法
    SUB,    // 减法
    MUL,    // 乘法
    DIV,    // 除法
    LDC,    // 加载常数
    INVALID // 无效指令
};

struct Instruction {
    BinIns op;  // 操作码
    int arg;    // 参数
};

2.2 主要数据结构

反汇编器类中包含两个关键的映射表:

cpp 复制代码
class Disassembler {
private:
    // 指令映射表(从二进制到汇编)
    std::map<BinIns, std::string> binToAss;
    // 指令参数个数表
    std::map<BinIns, int> insArgNum;
};
  • binToAss: 存储二进制操作码到汇编指令字符串的映射
  • insArgNum: 记录每个指令需要的参数个数

3. 核心功能实现

3.1 初始化映射表

cpp 复制代码
void initMapping() {
    binToAss[BinIns::HALT] = "HALT";
    binToAss[BinIns::IN]   = "IN";
    binToAss[BinIns::OUT]  = "OUT";
    binToAss[BinIns::ADD]  = "ADD";
    binToAss[BinIns::SUB]  = "SUB";
    binToAss[BinIns::MUL]  = "MUL";
    binToAss[BinIns::DIV]  = "DIV";
    binToAss[BinIns::LDC]  = "LDC";
}

3.2 初始化参数个数表

cpp 复制代码
void initArgNumbers() {
    insArgNum[BinIns::HALT] = 0;
    insArgNum[BinIns::IN]   = 0;
    insArgNum[BinIns::OUT]  = 0;
    insArgNum[BinIns::ADD]  = 0;
    insArgNum[BinIns::SUB]  = 0;
    insArgNum[BinIns::MUL]  = 0;
    insArgNum[BinIns::DIV]  = 0;
    insArgNum[BinIns::LDC]  = 1;
}

3.3 单条指令反汇编

cpp 复制代码
std::string disassembleLine(const Instruction& inst) {
    if (inst.op == BinIns::INVALID) {
        return "; INVALID INSTRUCTION";
    }

    auto it = binToAss.find(inst.op);
    if (it == binToAss.end()) {
        return "; UNKNOWN INSTRUCTION";
    }

    std::string result = it->second;
    if (insArgNum[inst.op] > 0) {
        result += " " + std::to_string(inst.arg);
    }

    return result;
}

这个函数是反汇编器的核心,它:

  1. 检查指令的有效性
  2. 查找对应的汇编指令字符串
  3. 如果指令需要参数,添加参数值
  4. 返回完整的汇编指令字符串

3.4 程序反汇编

cpp 复制代码
std::vector<std::string> disassembleProgram(const std::vector<Instruction>& program) {
    std::vector<std::string> lines;
    for (const auto& inst : program) {
        lines.push_back(disassembleLine(inst));
    }
    return lines;
}

3.5 Main函数

cpp 复制代码
#include "../include/assembler.hpp"
#include "../include/disassembler.hpp"
#include <fstream>
#include <vector>
#include <string>

// 从文件读取程序
std::vector<std::string> readFile(const std::string& filename) {
    std::vector<std::string> lines;
    std::ifstream file(filename);
    if (!file) {
        throw std::runtime_error("Cannot open file: " + filename);
    }

    std::string line;
    while (std::getline(file, line)) {
        lines.push_back(line);
    }
    return lines;
}

int main(int argc, char* argv[]) {
    if (argc != 3) {
        std::cout << "Usage: " << argv[0] << " <input.asm> <mode>" << std::endl;
        std::cout << "Mode: asm - assemble, dis - disassemble" << std::endl;
        return 1;
    }

    try {
        // 读取源文件
        auto sourceCode = readFile(argv[1]);
        std::string mode = argv[2];

        if (mode == "asm") {
            // 显示源代码
            std::cout << "Source code:" << std::endl;
            for (const auto& line : sourceCode) {
                std::cout << line << std::endl;
            }
            std::cout << std::endl;

            // 创建汇编器并汇编代码
            Assembler assembler;
            auto program = assembler.assembleProgram(sourceCode);

            // 显示汇编结果
            std::cout << "Assembled code:" << std::endl;
            assembler.outputBinary(program);
        }
        else if (mode == "dis") {
            // 创建汇编器和反汇编器
            Assembler assembler;
            Disassembler disassembler;
            
            // 先汇编代码(这里假设输入是汇编代码)
            auto program = assembler.assembleProgram(sourceCode);
            
            // 显示反汇编结果
            std::cout << "Disassembled code:" << std::endl;
            disassembler.outputAssembly(program);
        }
        else {
            std::cout << "Invalid mode. Use 'asm' for assembly or 'dis' for disassembly." << std::endl;
            return 1;
        }

    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

4. 测试方式

4.1 命令行接口

程序支持两种模式:

bash 复制代码
./assembler <input.asm> asm    # 汇编模式
./assembler <input.asm> dis    # 反汇编模式

5 测试结果

assembly 复制代码
你的文件目录 .\build\assembler.exe test/test_disasm.asm dis
Disassembled code:
LDC 42
IN
ADD
OUT
LDC 10
MUL
OUT
LDC 2
DIV
SUB
OUT
HALT
5.1 指令集说明
指令 参数 功能描述
LDC 数值 将一个常数加载到栈中
IN 从输入读取一个数字到栈中
ADD 将栈顶的两个数字相加
OUT 输出栈顶的结果
MUL 将栈顶的两个数字相乘
DIV 将栈顶的两个数字相除
SUB 将栈顶的两个数字相减
HALT 停止程序执行
5.1 指令序列
assembly 复制代码
LDC 42    ; 加载常数42
IN        ; 读取用户输入
ADD       ; 相加
OUT       ; 输出
LDC 10    ; 加载常数10
MUL       ; 相乘
OUT       ; 输出
LDC 2     ; 加载常数2
DIV       ; 相除
SUB       ; 相减
OUT       ; 输出
HALT      ; 结束
5.2 执行步骤
  1. 将常数42和用户输入的数字相加
  2. 输出第一次计算结果
  3. 将上一步结果乘以10
  4. 输出第二次计算结果
  5. 将上一步结果除以2
  6. 减去最初的输入值
  7. 输出最终结果
  8. 程序结束
5.3 示例执行

假设用户输入值为8,程序的执行过程如下:

步骤 操作 计算过程 结果
1 加法 42 + 8 50
2 输出 输出50 50
3 乘法 50 * 10 500
4 输出 输出500 500
5 除法 500 / 2 250
6 减法 250 - 8 242
7 输出 输出242 242
8 结束 程序终止 -
5.4 栈操作说明

每个操作都会影响栈的状态:

  • LDC: 将数值压入栈顶
  • IN: 将输入值压入栈顶
  • ADD/MUL/DIV/SUB: 弹出栈顶两个值,计算后将结果压回栈顶
  • OUT: 输出栈顶值(但不弹出)
  • HALT: 终止程序执行
相关推荐
ACERT3333 小时前
1.吴恩达机器学习笔记week1-2(线性回归模型及Sklearn的使用)
人工智能·python·机器学习
lxmyzzs3 小时前
【图像算法 - 27】基于YOLOv12与OpenCV的无人机智能检测系统
人工智能·深度学习·yolo·目标检测·无人机
测试者家园3 小时前
智能缓存架构:AI预测数据热点
人工智能·缓存·架构·系统优化·devops·架构设计·智能化测试
szxinmai主板定制专家3 小时前
基于RK3588与ZYNQ7045的ARM+FPGA+AI实时系统解决方案
arm开发·人工智能·嵌入式硬件·fpga开发
禁默3 小时前
第九届电气、机械与计算机工程国际学术会议(ICEMCE 2025)
人工智能·机械·电气·计算机工程
聚客AI3 小时前
🌈破解数据瓶颈!看AI Agent如何让人人成为数据分析师
人工智能·llm·agent
SmartBrain4 小时前
华为昇腾 910 到 950 系列 NPU 深度解析
人工智能·华为·gpu算力
金井PRATHAMA4 小时前
知识图谱对自然语言处理中深层语义分析的影响与启示
人工智能·自然语言处理·知识图谱
断水客4 小时前
如何自动生成ONNX模型?
人工智能·ai·onnx·ai编译器