首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

用 C 語言實作簡易虛擬機:從指令集設計到完整原始碼解析

     編輯:關於C語言

前言:為什麼要親手打造一台虛擬機?

在學習計算機組織與系統程式時,閱讀教科書上的理論固然重要,但若能親手實作一台虛擬機(Virtual Machine, VM),將指令集架構、記憶體管理、暫存器運作等核心概念落地為可執行的程式碼,理解深度將大不相同。本文將帶您從零開始,設計一個擁有 100 字節記憶體、五大核心暫存器、12 條指令的簡易虛擬機,並完整呈現 C++ 實作細節與執行範例。

虛擬機架構設計

記憶體與暫存器配置

這台虛擬機擁有 100 個字(word)的線性記憶體空間,索引範圍 0~99。核心暫存器包含:

  • 指令計數器 (IC):指向下一條將要執行的指令地址
  • 指令暫存器 (IR):暫存當前從記憶體取出的完整指令
  • 操作碼暫存器 (Opcode):指令的運算碼部分(高兩位)
  • 記憶體位址暫存器 (Address):指令的操作數位址部分(低兩位)
  • 累加暫存器 (Accumulator):算術邏輯運算的核心暫存器

指令集定義(共 12 條指令)

操作碼助憶符功能說明
01INPUT從標準輸入讀取整數,存入指定記憶體位址
02OUTPUT將指定記憶體位址的整數輸出至標準輸出
03LOAD將指定記憶體位址的資料載入累加器
04STORE將累加器的資料存入指定記憶體位址
05ADD累加器 += 指定記憶體位址的資料
06SUB累加器 -= 指定記憶體位址的資料
07MUL累加器 *= 指定記憶體位址的資料
08DIV累加器 /= 指定記憶體位址的資料(整數除法)
09BRANCH無條件跳轉至指定位址
10BRNEG若累加器 < 0 則跳轉至指定位址
11BRZERO若累加器 == 0 則跳轉至指定位址
12HALT終止程式執行

指令編碼採用四位數十進制表示:前兩位為操作碼,後兩位為記憶體位址。例如 0320 代表 LOAD 指令,操作位址 20。

範例程式:找出兩數中的較大值

以下組合語言程式示範如何輸入兩個數字、比較大小並輸出較大者:

0120  // 輸入第一個數到記憶體 20
0121  // 輸入第二個數到記憶體 21
0320  // 載入記憶體 20 到累加器
0621  // 累加器減去記憶體 21
1007  // 若累加器 < 0 跳轉至位址 07
0220  // 輸出記憶體 20(較大值)
1200  // 終止
0221  // 輸出記憶體 21(較大值)
1200  // 終止
0000  // 程式輸入結束標記

執行流程邏輯:先將兩數分別存入位址 20、21,再將位址 20 的值載入累加器並減去位址 21 的值。若結果為負(表示 20 < 21),跳轉至位址 07 輸出位址 21 的值;否則繼續執行輸出位址 20 的值。

完整 C++ 實作原始碼

主程式 leaf_core_test.cpp

#include <iostream>
#include "leaf_core_virtual_machine.hpp"

using namespace std;

int main(int argc, char *argv[])
{
    CVirtualMachine virtual_machine;  // 建立虛擬機物件
    int program[100];                 // 存放使用者輸入的程式
    int buffer;

    // 啟動虛擬機
    virtual_machine.boot();

    // 互動式輸入程式碼
    cout << "Please enter your program" << endl;
    cout << "00 ";
    cin >> buffer;
    for (int index = 0; (program[index] = buffer) != 0; index++) {
        cout << (index + 1) / 10 << (index + 1) % 10 << " ";
        cin >> buffer;
    }
    cout << "Good job." << endl;

    // 載入、列印、執行程式
    virtual_machine.load_program(program);
    virtual_machine.print_program();
    virtual_machine.run_program();

    // 關閉虛擬機
    virtual_machine.shut();

    char ch;
    cout << "Window closing" << endl;
    cin >> ch;
    return 0;
}

虛擬機類別宣告 leaf_core_virtual_machine.hpp

#ifndef __leaf_core_virtual_machine_hpp__
#define __leaf_core_virtual_machine_hpp__

class CVirtualMachine {
private:
    int m_memory[100];                // 記憶體
    int m_instruction_counter;        // 指令計數器 (IC)
    int m_instruction_register;       // 指令暫存器 (IR)
    int m_opcode;                     // 操作碼
    int m_address;                    // 記憶體位址
    int m_accumulator;                // 累加器

public:
    // 虛擬機操作碼常數定義
    static const int const_input = 1;
    static const int const_print = 2;
    static const int const_load = 3;
    static const int const_store = 4;
    static const int const_plus = 5;
    static const int const_minus = 6;
    static const int const_multiply = 7;
    static const int const_divide = 8;
    static const int const_branch = 9;
    static const int const_branch_below = 10;
    static const int const_branch_zero = 11;
    static const int const_halt = 12;

public:
    CVirtualMachine();
    ~CVirtualMachine();

    void boot();                      // 啟動虛擬機
    void shut();                      // 關閉虛擬機
    void load_program(int *program);  // 載入程式到記憶體
    void print_program();             // 列印記憶體中的程式
    void run_program();               // 執行程式
};

#endif

虛擬機核心邏輯 leaf_core_virtual_machine.cpp(節錄關鍵方法)

#include "leaf_core_virtual_machine.hpp"
#include <iostream>

using namespace std;

// 建構子:初始化所有暫存器為 0
CVirtualMachine::CVirtualMachine()
{
    m_instruction_counter = 0;
    m_instruction_register = 0;
    m_opcode = 0;
    m_address = 0;
    m_accumulator = 0;
}

CVirtualMachine::~CVirtualMachine() {}

// 啟動與關閉:單純列印狀態訊息
void CVirtualMachine::boot()
{
    cout << "Virtual machine is booting..." << endl;
    cout << "Complete booting." << endl;
}

void CVirtualMachine::shut()
{
    cout << "Virtual machine is shutting..." << endl;
    cout << "Complete shutting." << endl;
}

// 將使用者輸入的程式陣列複製到虛擬機記憶體
void CVirtualMachine::load_program(int *program)
{
    int index;
    cout << "Virtual machine is loading the program..." << endl;
    for (index = 0; index < 100 && program[index] != 0; index++) {
        m_memory[index] = program[index];
    }
    m_memory[index] = program[index];  // 複製結束標記 0000
    cout << "Complete loading." << endl;
}

// 列印記憶體內容(指令地址與機器碼)
void CVirtualMachine::print_program()
{
    int index;
    cout << "Virtual machine is printing the program..." << endl;
    for (index = 0; index < 100 && m_memory[index] != 0; index++) {
        cout << index / 10 << index % 10 << " " << m_memory[index] << endl;
    }
    cout << "Complete printing." << endl;
}

// 核心執行迴圈:取指 -> 解碼 -> 執行
void CVirtualMachine::run_program()
{
    cout << "Virtual machine is running the program..." << endl;
    cout << "opcode address" << endl;

    while (true) {
        // 取指階段
        m_instruction_register = m_memory[m_instruction_counter];
        m_opcode = m_instruction_register / 100;      // 取高兩位
        m_address = m_instruction_register % 100;     // 取低兩位

        cout << m_opcode << "        " << m_address << endl;

        // 執行階段
        switch (m_opcode) {
            case const_input:   // 01 INPUT
                cout << "? ";
                cin >> m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_print:   // 02 OUTPUT
                cout << m_memory[m_address] << endl;
                m_instruction_counter++;
                break;

            case const_load:    // 03 LOAD
                m_accumulator = m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_store:   // 04 STORE
                m_memory[m_address] = m_accumulator;
                m_instruction_counter++;
                break;

            case const_plus:    // 05 ADD
                m_accumulator += m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_minus:   // 06 SUB
                m_accumulator -= m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_multiply: // 07 MUL
                m_accumulator *= m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_divide:  // 08 DIV
                if (m_memory[m_address] != 0)
                    m_accumulator /= m_memory[m_address];
                m_instruction_counter++;
                break;

            case const_branch:  // 09 BRANCH
                m_instruction_counter = m_address;
                break;

            case const_branch_below: // 10 BRNEG
                if (m_accumulator < 0)
                    m_instruction_counter = m_address;
                else
                    m_instruction_counter++;
                break;

            case const_branch_zero:  // 11 BRZERO
                if (m_accumulator == 0)
                    m_instruction_counter = m_address;
                else
                    m_instruction_counter++;
                break;

            case const_halt:    // 12 HALT
                cout << "Complete running" << endl;
                return;

            default:
                m_instruction_counter++;
                break;
        }
    }
}

執行畫面範例

Virtual machine is booting...
Complete booting.

Please enter your program.
00 0120
01 0121
02 0320
03 0621
04 1007
05 0220
06 1200
07 0221
08 1200
09 0000
Good job.

Virtual machine is loading the program...
Complete loading.

Virtual machine is printing the program...
00 0120
01 0121
02 0320
03 0621
04 1007
05 0220
06 1200
07 0221
08 1200
Complete printing.

Virtual machine is running the program...
opcode address
01        20
100
01        21
200
03        20
06        21
10        07
02        21
200
Complete running

Virtual machine is shutting...
Complete shutting.

延伸思考與改進方向

這個簡易虛擬機雖然功能有限,但完整展示了「取指-解碼-執行」的經典 CPU 週期。若要進一步擴充,可考慮:

  • 支援更多資料型別(浮點數、字元、字串)
  • 加入堆疊指令(PUSH/POP/CALL/RET)實現子程式呼叫
  • 實作中斷機制與 I/O 裝置模擬
  • 設計簡易組譯器,支援標籤與符號化位址
  • 移植至嵌入式平台(如 Arduino、STM32)作為教學用微核心

親手打造虛擬機不僅能深化對計算機架構的理解,更是編譯器、作業系統、嵌入式開發等進階領域的敲門磚。建議讀者嘗試修改指令集、新增指令,或嘗試用其他語言(Rust、Go、Python)重寫,體會不同語言特性對系統程式設計的影響。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved