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

这个秋季,三碗饭会开始更新自己在秋季课程的笔记,主要包括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: 终止程序执行
相关推荐
Light604 小时前
破局而立:制造业软件企业的模式重构与AI赋能新路径
人工智能·云原生·工业软件·商业模式创新·ai赋能·人机协同·制造业软件
Quintus五等升4 小时前
深度学习①|线性回归的实现
人工智能·python·深度学习·学习·机器学习·回归·线性回归
natide4 小时前
text-generateion-webui模型加载器(Model Loaders)选项
人工智能·llama
野生的码农5 小时前
码农的妇产科实习记录
android·java·人工智能
TechubNews5 小时前
2026 年观察名单:基于 a16z「重大构想」,详解稳定币、RWA 及 AI Agent 等 8 大流行趋势
大数据·人工智能·区块链
脑极体5 小时前
机器人的罪与罚
人工智能·机器人
三不原则5 小时前
故障案例:容器启动失败排查(AI运维场景)——从日志分析到根因定位
运维·人工智能·kubernetes
点云SLAM5 小时前
凸优化(Convex Optimization)理论(1)
人工智能·算法·slam·数学原理·凸优化·数值优化理论·机器人应用
会周易的程序员5 小时前
多模态AI 基于工业级编译技术的PLC数据结构解析与映射工具
数据结构·c++·人工智能·单例模式·信息可视化·架构
BlockWay5 小时前
WEEX 成为 LALIGA 西甲联赛香港及台湾地区官方区域合作伙伴
大数据·人工智能·安全