在學習計算機組織與系統程式時,閱讀教科書上的理論固然重要,但若能親手實作一台虛擬機(Virtual Machine, VM),將指令集架構、記憶體管理、暫存器運作等核心概念落地為可執行的程式碼,理解深度將大不相同。本文將帶您從零開始,設計一個擁有 100 字節記憶體、五大核心暫存器、12 條指令的簡易虛擬機,並完整呈現 C++ 實作細節與執行範例。
這台虛擬機擁有 100 個字(word)的線性記憶體空間,索引範圍 0~99。核心暫存器包含:
| 操作碼 | 助憶符 | 功能說明 |
|---|---|---|
| 01 | INPUT | 從標準輸入讀取整數,存入指定記憶體位址 |
| 02 | OUTPUT | 將指定記憶體位址的整數輸出至標準輸出 |
| 03 | LOAD | 將指定記憶體位址的資料載入累加器 |
| 04 | STORE | 將累加器的資料存入指定記憶體位址 |
| 05 | ADD | 累加器 += 指定記憶體位址的資料 |
| 06 | SUB | 累加器 -= 指定記憶體位址的資料 |
| 07 | MUL | 累加器 *= 指定記憶體位址的資料 |
| 08 | DIV | 累加器 /= 指定記憶體位址的資料(整數除法) |
| 09 | BRANCH | 無條件跳轉至指定位址 |
| 10 | BRNEG | 若累加器 < 0 則跳轉至指定位址 |
| 11 | BRZERO | 若累加器 == 0 則跳轉至指定位址 |
| 12 | HALT | 終止程式執行 |
指令編碼採用四位數十進制表示:前兩位為操作碼,後兩位為記憶體位址。例如 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 的值。
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 週期。若要進一步擴充,可考慮:
親手打造虛擬機不僅能深化對計算機架構的理解,更是編譯器、作業系統、嵌入式開發等進階領域的敲門磚。建議讀者嘗試修改指令集、新增指令,或嘗試用其他語言(Rust、Go、Python)重寫,體會不同語言特性對系統程式設計的影響。