当前位置: 首页 > news >正文

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

前言:

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

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

1: 概要

了解下之后我们会完成的三个内容。
汇编器:
将人类可读的汇编代码(.uma)转换为机器码(.um)
相当于"翻译官",把人类语言翻译成机器语言

反汇编器
将机器码(.um)转换回汇编代码(.uma)
相当于"解码器",把机器语言翻译回人类语言
主要用于调试和验证

模拟器:
执行机器码(.um)
相当于"虚拟CPU",实际运行程序
负责程序的实际执行

2. 指令类型定义

enum class BinIns {HALT,   // 停止程序IN,     // 输入OUT,    // 输出ADD,    // 加法SUB,    // 减法MUL,    // 乘法DIV,    // 除法LDC,    // 加载常数INVALID // 无效指令
};
  • 使用枚举类定义所有支持的指令类型
  • 每个指令都有一个对应的数值(从0开始)
  • enum class提供类型安全,避免与其他枚举混淆

3. 指令结构体

struct Instruction {BinIns op;  // 操作码:表示指令类型int arg;    // 参数:存储指令的参数值// 构造函数:创建新指令时使用Instruction(BinIns op_ = BinIns::HALT, int arg_ = 0) : op(op_), arg(arg_) {}
};
  • op:存储指令类型(如ADD, SUB等)
  • arg:存储指令参数(如LDC 3中的3)
  • 默认构造函数创建HALT指令

4. 汇编器类

class Assembler {
private:// 存储名称std::vector<std::string> assIns;// 记录每个指令需要的参数个数std::map<BinIns, int> insArgNum;// 指令名称到操作码的映射std::map<std::string, BinIns> assToBin;

4.1 初始化函数

void initInstructions() {assIns = {"HALT", "IN", "OUT", "ADD", "SUB", "MUL", "DIV", "LDC"};
}
  • 初始化所有支持的指令名称
  • 这些是用户在汇编代码中使用的指令
void initArgNumbers() {insArgNum[BinIns::HALT] = 0;  // HALT不需要参数insArgNum[BinIns::IN]   = 0;  // IN不需要参数insArgNum[BinIns::OUT]  = 0;  // OUT不需要参数insArgNum[BinIns::ADD]  = 0;  // ADD不需要参数insArgNum[BinIns::SUB]  = 0;  // SUB不需要参数insArgNum[BinIns::MUL]  = 0;  // MUL不需要参数insArgNum[BinIns::DIV]  = 0;  // DIV不需要参数insArgNum[BinIns::LDC]  = 1;  // LDC需要1个参数
}
  • 定义每个指令需要的参数个数
  • 0表示不需要参数,1表示需要一个参数
void initMapping() {assToBin["HALT"] = BinIns::HALT;assToBin["IN"]   = BinIns::IN;assToBin["OUT"]  = BinIns::OUT;// 其他指令映射
}
  • 建立指令文本到操作码的映射
  • 用于将汇编代码转换为机器指令

4.2 汇编单行代码

Instruction assembleLine(const std::string& line) {std::istringstream iss(line);  // 创建字符串流std::string op;                // 存储指令名iss >> op;                     // 读取指令名// 转换为大写(这样halt和HALT都能识别)for (char& c : op) {c = toupper(c);}// 跳过空行和注释if (op.empty() || op[0] == ';') {return Instruction(BinIns::INVALID);}// 查找指令是否存在auto it = assToBin.find(op);if (it == assToBin.end()) {std::cerr << "Unknown instruction: " << op << std::endl;return Instruction(BinIns::INVALID);}BinIns binOp = it->second;  // 获取操作码int arg = 0;                // 默认参数值// 如果指令需要参数,读取参数if (insArgNum[binOp] > 0) {iss >> arg;}return Instruction(binOp, arg);  // 创建并返回指令
}

4.3 汇编整个程序

std::vector<Instruction> assembleProgram(const std::vector<std::string>& lines) {std::vector<Instruction> program;  // 存储生成的指令for (const auto& line : lines) {   // 处理每一行if (!line.empty() && line[0] != ';') {  // 跳过空行和注释auto inst = assembleLine(line);      // 汇编这一行if (inst.op != BinIns::INVALID) {   // 如果是有效指令program.push_back(inst);         // 添加到程序中}}}return program;  // 返回完整的程序
}

4.4 输出二进制格式

void outputBinary(const std::vector<Instruction>& program) {for (const auto& inst : program) {// 输出操作码std::cout << "Op: " << static_cast<int>(inst.op) << " (" << getBinString(static_cast<int>(inst.op), 8) << ")";// 如果指令有参数,输出参数if (insArgNum[inst.op] > 0) {std::cout << "\tArg: " << inst.arg << " (" << getBinString(inst.arg, 32) << ")";}std::cout << std::endl;}
}

4.5 辅助函数

std::string getBinString(int n, int bits) {std::string result(bits, '0');  // 创建指定长度的字符串,初始全为'0'// 从右到左填充二进制位for (int i = bits - 1; i >= 0 && n > 0; i--, n /= 2) {result[i] = (n % 2) + '0';  // 转换为字符'0'或'1'}return result;
}
  • 将数字转换为二进制字符串
  • bits指定输出的位数
  • 用于格式化输出

5. 使用示例

// 创建汇编器
Assembler assembler;// 汇编一行代码
Instruction inst = assembler.assembleLine("LDC 42");
// 结果:操作码 = LDC (7), 参数 = 42// 汇编一个程序
std::vector<std::string> program = {"LDC 3","OUT","HALT"
};
auto binary = assembler.assembleProgram(program);
// 结果:[{LDC,3}, {OUT,0}, {HALT,0}]
完整代码块
#ifndef ASSEMBLER_HPP
#define ASSEMBLER_HPP#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>// 指令类型枚举
enum class BinIns {HALT,   // 停止程序IN,     // 输入OUT,    // 输出ADD,    // 加法SUB,    // 减法MUL,    // 乘法DIV,    // 除法LDC,    // 加载常数INVALID // 无效指令
};// 指令结构
struct Instruction {BinIns op;  // 操作码int arg;    // 参数Instruction(BinIns op_ = BinIns::HALT, int arg_ = 0) : op(op_), arg(arg_) {}
};class Assembler {
private:// 指令集std::vector<std::string> assIns;// 指令参数个数表std::map<BinIns, int> insArgNum;// 指令映射表std::map<std::string, BinIns> assToBin;public:Assembler() {initInstructions();initArgNumbers();initMapping();}// 汇编一行代码Instruction assembleLine(const std::string& line) {std::istringstream iss(line);std::string op;iss >> op;// 转换为大写for (char& c : op) {c = toupper(c);}// 跳过注释if (op.empty() || op[0] == ';') {return Instruction(BinIns::INVALID);}// 查找指令auto it = assToBin.find(op);if (it == assToBin.end()) {std::cerr << "Unknown instruction: " << op << std::endl;return Instruction(BinIns::INVALID);}BinIns binOp = it->second;int arg = 0;// 如果指令需要参数,读取参数if (insArgNum[binOp] > 0) {iss >> arg;}return Instruction(binOp, arg);}// 汇编整个程序std::vector<Instruction> assembleProgram(const std::vector<std::string>& lines) {std::vector<Instruction> program;for (const auto& line : lines) {if (!line.empty() && line[0] != ';') {auto inst = assembleLine(line);if (inst.op != BinIns::INVALID) {program.push_back(inst);}}}return program;}// 输出二进制格式void outputBinary(const std::vector<Instruction>& program) {for (const auto& inst : program) {std::cout << "Op: " << static_cast<int>(inst.op) << " (" << getBinString(static_cast<int>(inst.op), 8) << ")";if (insArgNum[inst.op] > 0) {std::cout << "\tArg: " << inst.arg << " (" << getBinString(inst.arg, 32) << ")";}std::cout << std::endl;}}private:// 初始化指令集void initInstructions() {assIns = {"HALT", "IN", "OUT", "ADD", "SUB", "MUL", "DIV", "LDC"};}// 初始化参数个数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;}// 初始化指令映射void initMapping() {assToBin["HALT"] = BinIns::HALT;assToBin["IN"]   = BinIns::IN;assToBin["OUT"]  = BinIns::OUT;assToBin["ADD"]  = BinIns::ADD;assToBin["SUB"]  = BinIns::SUB;assToBin["MUL"]  = BinIns::MUL;assToBin["DIV"]  = BinIns::DIV;assToBin["LDC"]  = BinIns::LDC;}// 获取二进制字符串表示std::string getBinString(int n, int bits) {std::string result(bits, '0');for (int i = bits - 1; i >= 0 && n > 0; i--, n /= 2) {result[i] = (n % 2) + '0';}return result;}
};#endif // ASSEMBLER_HPP

Main

#include "../include/assembler.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 != 2) {std::cout << "Usage: " << argv[0] << " <input.asm>" << std::endl;return 1;}try {// 读取源文件auto sourceCode = readFile(argv[1]);// 显示源代码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);} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return 1;}return 0;
}

测试文件
test

; 打印 ; 测试程序
; 计算 (3 + 4) * 2LDC 3       ; 加载数字3
OUT         ; 输出当前值(用于调试)
LDC 4       ; 加载数字4
ADD         ; 3 + 4
OUT         ; 输出当前值(用于调试)
LDC 2       ; 加载数字2
MUL         ; (3 + 4) * 2
OUT         ; 输出结果
HALT        ; 停止程序

具体输出:

Op: 7 (00000111) Arg: 3 (00000000000000000000000000000011)    ; LDC 3
Op: 2 (00000010)                                               ; OUT
Op: 7 (00000111) Arg: 4 (00000000000000000000000000000100)    ; LDC 4
Op: 3 (00000011)                                               ; ADD
Op: 2 (00000010)                                               ; OUT
Op: 7 (00000111) Arg: 2 (00000000000000000000000000000010)    ; LDC 2
Op: 5 (00000101)                                               ; MUL
Op: 2 (00000010)                                               ; OUT
Op: 0 (00000000)                                               ; HALT
1. `LDC 3`:- 操作码:7(LDC)- 参数:3- 动作:将3加载到寄存器2. `OUT`:- 操作码:2(OUT)- 动作:输出当前值(33. `LDC 4`:- 操作码:7(LDC)- 参数:4- 动作:将4加载到寄存器4. `ADD`:- 操作码:3(ADD)- 动作:3 + 4 = 75. `OUT`:- 操作码:2(OUT)- 动作:输出当前值(76. `LDC 2`:- 操作码:7(LDC)- 参数:2- 动作:将2加载到寄存器7. `MUL`:- 操作码:5(MUL)- 动作:7 * 2 = 148. `OUT`:- 操作码:2(OUT)- 动作:输出当前值(149. `HALT`:- 操作码:0(HALT)- 动作:停止程序
如果执行这个程序,会依次输出:

程序运行结果

如果执行这个程序,会依次输出:

3   (第一个OUT的结果)
7   (第二个OUT的结果,3+4)
14  (第三个OUT的结果,7*2)

参考:
https://www.cnblogs.com/unixfy/p/3357784.html
https://blog.csdn.net/yuqian123455/category_8798607.html


文章转载自:

http://4bsa3p6i.Lynmt.cn
http://6w17L2WJ.Lynmt.cn
http://x1YYRoR3.Lynmt.cn
http://CSu5a9eC.Lynmt.cn
http://dBsNDdbS.Lynmt.cn
http://Ev1oScJy.Lynmt.cn
http://Z81lMpri.Lynmt.cn
http://Cv1XL6LB.Lynmt.cn
http://OQubK5KY.Lynmt.cn
http://rQpueXId.Lynmt.cn
http://qSNHkM1G.Lynmt.cn
http://exA3EBo0.Lynmt.cn
http://4qJteSdf.Lynmt.cn
http://0ixB7eCo.Lynmt.cn
http://2N8bLQqM.Lynmt.cn
http://6MenEhIZ.Lynmt.cn
http://nbJ1R2Pj.Lynmt.cn
http://lGWVCRnU.Lynmt.cn
http://hm2ad4kD.Lynmt.cn
http://sDfMylzW.Lynmt.cn
http://sh4XnWir.Lynmt.cn
http://Ip8IErV5.Lynmt.cn
http://hbuSvBdW.Lynmt.cn
http://TYlakPPB.Lynmt.cn
http://UAkFZMnG.Lynmt.cn
http://p7VsPixR.Lynmt.cn
http://NALLGvOH.Lynmt.cn
http://Pdbwi1Zv.Lynmt.cn
http://ACCAoh94.Lynmt.cn
http://ttg74zBV.Lynmt.cn
http://www.dtcms.com/a/373742.html

相关文章:

  • 【Ubuntu20.04 + VS code 1.103.2 最新版,中文输入法失效】
  • 【开题答辩全过程】以 基于Python的北城公务用车系统设计与实现_为例,包含答辩的问题和答案
  • Proximal SFT:用PPO强化学习机制优化SFT,让大模型训练更稳定
  • 2025年Q3 GEO优化供应商技术能力评估与行业应用指南
  • 25上半年软考网工备考心得
  • XPath:从入门到能用
  • Kotlin协程 -> Job.join() 完整流程图与核心源码分析
  • [优选算法专题二滑动窗口——串联所有单词的子串]
  • VR森林防火模拟进行零风险演练,成本降低​
  • 玩转Docker | 使用Docker部署Kener状态页监控工具
  • Oracle 官网账号登不了?考过的证书还能下载吗?
  • Oracle 数据库高级查询语句方法
  • WSD3075DN56高性能MOS管在汽车电动助力转向系统(EPS)中的应用
  • 1.1 汽车运行滚动阻力
  • LinuxC++项目开发日志——高并发内存池(3-thread cache框架开发)
  • Android 自定义 TagView
  • 下沉一线强赋能!晓商圈多维帮扶护航城市共建者
  • YOLO12 改进、魔改|通道自注意力卷积块CSA-ConvBlock,通过动态建模特征图通道间的依赖关系,优化通道权重分配,在强化有效特征、抑制冗余信息
  • 提升数据库性能的秘密武器:深入解析慢查询、连接池与Druid监控
  • 中间件的日志分析
  • 机器宠物外壳设计的详细流程
  • OpenCV C++ 二值图像分析:从连通组件到轮廓匹配
  • Java分页 Element—UI
  • Flow-GRPO: Training Flow Matching Models via Online RL
  • C#中解析XML时遇到注释节点报错
  • 联邦学习辅导流程
  • MySQL MVCC原理
  • QSS加载失败的奇葩问题--已解决
  • 一体化伺服电机在管道焊缝检测爬行机器人中的应用案例
  • flowable发起申请后无法查看申请记录