自製虛擬機是理解計算機體系結構、編譯原理與操作系統的最佳途徑之一。本文記錄如何用 C/C++ 從零開始搭建一個簡易的 16 位虛擬機框架,重點涵蓋 CPU 寄存器建模、指令反組識架構設計以及核心調度邏輯。
Register_16_Bit虛擬機的核心是 CPU 狀態的模擬。我們定義一個結構體封裝所有 16 位及 8 位寄存器,採用聯合體/嵌套結構體實現高低 8 位訪問(如 AX = AH + AL)。
#pragma once
#include "CommonBase.h" // 假設定義了 BYTE, USHORT 等類型
struct Register_16_Bit {
// 通用寄存器 (可拆分高低 8 位)
struct { BYTE AH, AL; } AX;
struct { BYTE BH, BL; } BX;
struct { BYTE CH, CL; } CX;
struct { BYTE DH, DL; } DX;
// 段寄存器
USHORT CS, DS, SS, ES;
// 專用寄存器
USHORT IP; // 指令指針
USHORT SP; // 堆棧指針
USHORT BP; // 基址指針
USHORT SI, DI; // 變址寄存器
// 標誌寄存器 (位域優化內存)
struct {
BYTE OF : 1; BYTE DF : 1; BYTE IF : 1; BYTE TF : 1;
BYTE SF : 1; BYTE ZF : 1; BYTE AF : 1; BYTE PF : 1;
BYTE CF : 1; BYTE Reserve : 7;
} FR;
// 建構子:零初始化
Register_16_Bit() { memset(this, 0, sizeof(Register_16_Bit)); }
};
設計亮點:使用位域 (: 1) 精確映射標誌寄存器每一位;嵌套結構體實現 16 位與 8 位寄存器的別名訪問,極大提升模擬真實感與編程便利性。
CDisasmContext採用策略模式,定義抽象基類統一反組識入口,將具體指令族(JMP, MOV 等)解耦。
class CDisasmContext {
public:
virtual bool Disasm(BYTE*& ptr) = 0; // 核心反組識函數,ptr 為指令流指針,會隨解析前移
protected:
enum OpCodeHeader; // 具體子類定義具體指令頭枚舉
};
CJmp 繼承 CDisasmContext,內部定義跳轉指令操作碼枚舉,並將不同跳轉類型(短跳、近跳、遠跳、間接跳)拆分為私有方法。
class CJmp : public CDisasmContext {
public:
virtual bool Disasm(BYTE*& ptr) override;
protected:
enum OpCodeHeader {
JMP_SHORT = 0xEB, // 段內短跳 (相對偏移 8 位)
JMP_NEAR = 0xE9, // 段內近跳 (相對偏移 16/32 位)
JMP_FAR = 0xEA, // 段間直接跳 (段:偏移)
JMP_IND = 0xFF // 間接跳 (ModR/M 形式)
};
bool Jmp_Short(BYTE*& ptr);
bool Jmp_Near(BYTE*& ptr);
bool Jmp_Far(BYTE*& ptr);
};
// 鏈式嘗試匹配
bool CJmp::Disasm(BYTE*& ptr) {
if (Jmp_Short(ptr)) return true;
if (Jmp_Near(ptr)) return true;
if (Jmp_Far(ptr)) return true;
return false; // 非 JMP 指令,交由下一個 Handler 處理
}
CDisasm核心驅動類,持有各指令族實例,按優先級順序嘗試解碼指令流。
class CDisasm {
public:
bool Disasm(BYTE* ptr, int size) {
BYTE* end = ptr + size;
while (ptr < end) {
// 嘗試順序決定了解碼優先級,通常複雜指令優先或按頻度排序
if (Jmp.Disasm(ptr)) continue;
else if (Mov.Disasm(ptr)) continue;
// ... 其他指令族 ...
else return false; // 遇到無法識別的指令碼,停止
}
return true;
}
protected:
CJmp Jmp;
CMov Mov; // 假設已實現
};
if-else 鏈替換為操作碼映射表 (std::map<BYTE, HandlerFunc>),實現 O(1) 查找,便於動態擴展指令集。這個框架雖簡,卻包含了虛擬機「狀態定義、指令解碼、執行調度」三大核心骨架。由此出發,可逐步實現完整的指令集模擬器,甚至自研編譯器後端目標機。