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

这个秋季,三碗饭会开始更新自己在秋季课程的笔记,主要包括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: 终止程序执行
相关推荐
QY_Research_3 分钟前
全球钥匙联锁系统 市场规模及CAGR5.2%深度分析
大数据·人工智能·物联网·安全·机器人
Even_ISP_Color5 分钟前
NAFNetLocal_池化窗口一致性
人工智能·深度学习·机器学习
白露与泡影6 分钟前
Spring AI Alibaba -- 文生图、文生音及向量化
java·人工智能·spring
AI你一生一世7 分钟前
当“版权主张”不再需要人类:从 Luanti 被下架看自动化维权的工程化风险
运维·人工智能·自动化·开源软件·内容分发·自动化维权·版权主张
港股研究社7 分钟前
换了多个AI之后,我的创作项目在豆包工作集合
人工智能
森山冶仁10 分钟前
数据飞轮的企业版切入:四个环节先做哪个
人工智能·数据治理·数据要素·数据质量·数据资产
xianghongtao011612 分钟前
DeepSeek成名之后的路
人工智能·智能体·deepseek·deepseek融资·端脑科技·商业新闻
linktokenview20 分钟前
GLM-5.3-Flash私有化部署实践指南
人工智能
W_3260021 分钟前
Python 图像处理第二十八章|OpenCV 形态学操作进阶:腐蚀、膨胀、开运算、闭运算、梯度、顶帽黑帽
图像处理·人工智能·python·opencv·计算机视觉
美狐美颜SDK开放平台23 分钟前
视频美颜SDK如何实现动态贴纸跟随?人脸追踪技术解析
android·人工智能·计算机视觉·音视频·直播美颜sdk