⚡ Mini Virtual Machine --- 可视化虚拟机模拟器(91行)
计算机底层核心:寄存器、内存、指令集、程序计数器、栈、标志位
<!DOCTYPE html>
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>MiniVM</title><style>
*{margin:0;padding:0;box-sizing:border-box;font-family:'Courier New',monospace}body{background:#0a0a1a;color:#0f0;padding:15px}
h1{text-align:center;color:#0ff;font-size:20px;margin-bottom:12px;text-shadow:0 0 20px #0ff}
.wrap{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;max-width:1150px;margin:0 auto}
.p{background:rgba(0,255,100,.03);border:1px solid #0f03;border-radius:8px;padding:12px;margin-bottom:12px}
.p h3{color:#0ff;font-size:13px;border-bottom:1px solid #0f03;padding-bottom:4px;margin-bottom:8px}
textarea{width:100%;height:200px;background:#000;color:#0f0;border:1px solid #0f03;border-radius:4px;padding:8px;font:12px 'Courier New';resize:none}
.bs{display:flex;gap:6px;margin-top:8px}.b{flex:1;padding:7px;border:1px solid;border-radius:4px;cursor:pointer;font:bold 12px 'Courier New';background:0 0;transition:.3s}
.b:hover{filter:brightness(1.5);box-shadow:0 0 12px currentColor}.b1{color:#0f0;border-color:#0f0}.b2{color:#0ff;border-color:#0ff}.b3{color:#f55;border-color:#f55}
.rg{display:grid;grid-template-columns:1fr 1fr;gap:5px}.ri{background:#0001;padding:5px 8px;border-radius:4px;display:flex;justify-content:space-between;border:1px solid #0f02;font-size:12px}
.ri.a{border-color:#ff0;background:rgba(255,255,0,.12);color:#ff0}.ri span:first-child{color:#888}
.mg{display:grid;grid-template-columns:repeat(8,1fr);gap:2px;font-size:10px}.mc{background:#0001;padding:3px;text-align:center;border-radius:2px;border:1px solid transparent}
.mc.a{border-color:#f0f;background:rgba(255,0,255,.15);color:#f0f}.mc.pc{border-color:#0ff;background:rgba(0,255,255,.15);color:#0ff}
.mc i{color:#444;font-size:8px;display:block;font-style:normal}.out{background:#000;padding:8px;border-radius:4px;min-height:50px;font-size:12px;color:#ff0;border:1px solid #ff03}
.sk{background:#0001;padding:3px 8px;margin:2px 0;border-radius:3px;border-left:3px solid #f0f;font-size:11px}
.lg{font-size:10px;color:#888;max-height:100px;overflow-y:auto}.lg div{padding:1px 0;border-bottom:1px solid #fff1}
</style></head><body><h1>⚡ MiniVM — 可视化虚拟机模拟器</h1><div class="wrap"><div>
<div class="p"><h3>📝 汇编程序</h3><textarea id="code">MOV R0 10
MOV R1 1
MOV R2 0
LOOP:
ADD R2 R0
SUB R0 R1
PUSH R0
POP R3
CMP R0 0
JNE LOOP
OUT R2
HLT</textarea>
<div class="bs"><button class="b b1" οnclick="run()">▶ 运行</button><button class="b b2" οnclick="step()">⏭ 单步</button><button class="b b3" οnclick="reset()">⟲ 重置</button></div></div>
<div class="p"><h3>📤 输出</h3><div class="out" id="out"></div></div></div><div>
<div class="p"><h3>🔧 寄存器</h3><div class="rg" id="regs"></div></div>
<div class="p"><h3>📚 栈</h3><div id="stk"><span style="color:#555">(空)</span></div></div>
<div class="p"><h3>📋 日志</h3><div class="lg" id="log"></div></div></div>
<div class="p"><h3>💾 内存 (64B)</h3><div class="mg" id="mem"></div></div></div>
<script>
let R,PC,SP,ZF,mem,stack,prog,labels,out,halted,lastW;
function reset(){R={R0:0,R1:0,R2:0,R3:0};PC=0;SP=64;ZF=0;mem=Array(64).fill(0);stack=[];prog=[];labels={};out='';halted=false;lastW=-1;
document.getElementById('out').textContent='';document.getElementById('log').innerHTML='';parse();render()}
function parse(){let lines=document.getElementById('code').value.split('\n'),idx=0;prog=[];labels={};
lines.forEach(l=>{l=l.trim();if(!l)return;if(l.endsWith(':')){labels[l.slice(0,-1)]=idx}else{prog.push(l.split(/\s+/));idx++}})}
function val(v){return R.hasOwnProperty(v)?R[v]:parseInt(v)}
function exec(){if(halted||PC>=prog.length)return false;let ins=prog[PC],op=ins[0],a=ins[1],b=ins[2],info='';
switch(op){case'MOV':R[a]=val(b);info=`${a}=${val(b)}`;break;case'ADD':R[a]+=val(b);info=`${a}=${R[a]}`;break;
case'SUB':R[a]-=val(b);info=`${a}=${R[a]}`;break;case'MUL':R[a]*=val(b);info=`${a}=${R[a]}`;break;
case'AND':R[a]&=val(b);info=`${a}=${R[a]}`;break;case'OR':R[a]|=val(b);info=`${a}=${R[a]}`;break;
case'XOR':R[a]^=val(b);info=`${a}=${R[a]}`;break;case'NOT':R[a]=~R[a]&0xFF;info=`${a}=${R[a]}`;break;
case'SHL':R[a]<<=val(b);info=`${a}=${R[a]}`;break;case'SHR':R[a]>>=val(b);info=`${a}=${R[a]}`;break;
case'CMP':ZF=val(a)===val(b)?1:0;info=`ZF=${ZF}`;break;case'JMP':PC=labels[a];info=`→${a}`;return true;
case'JE':if(ZF){PC=labels[a];info=`→${a}`;return true}break;case'JNE':if(!ZF){PC=labels[a];info=`→${a}`;return true}break;
case'PUSH':SP--;mem[SP]=val(a);stack.unshift(val(a));lastW=SP;info=`[${SP}]=${val(a)}`;break;
case'POP':R[a]=mem[SP];stack.shift();lastW=SP;SP++;info=`${a}=${R[a]}`;break;
case'STORE':mem[val(b)]=val(a);lastW=val(b);info=`[${val(b)}]=${val(a)}`;break;case'LOAD':R[a]=mem[val(b)];info=`${a}=M[${val(b)}]`;break;
case'OUT':out+=val(a)+' ';document.getElementById('out').textContent=out;info=`输出:${val(a)}`;break;
case'HLT':halted=true;info='停机';break}PC++;
let lg=document.getElementById('log');lg.innerHTML=`<div><span style="color:#0ff">${op}</span> ${ins.slice(1).join(' ')} → ${info}</div>`+lg.innerHTML;return!halted}
function render(){let rh='';['R0','R1','R2','R3'].forEach(r=>rh+=`<div class="ri"><span>${r}</span><span>${R[r]}</span></div>`);
rh+=`<div class="ri a"><span>PC</span><span>${PC}</span></div><div class="ri"><span>SP</span><span>${SP}</span></div>`;
rh+=`<div class="ri"><span>ZF</span><span>${ZF}</span></div><div class="ri"><span>HLT</span><span>${halted?'✓':'—'}</span></div>`;
document.getElementById('regs').innerHTML=rh;let mh='';
for(let i=0;i<64;i++){let c=i===PC&&PC<prog.length?'pc':i===lastW?'a':'';mh+=`<div class="mc ${c}"><i>${i.toString(16).padStart(2,'0')}</i>${mem[i].toString(16).padStart(2,'0')}</div>`}
document.getElementById('mem').innerHTML=mh;let sh=stack.length?stack.map((v,i)=>`<div class="sk">${i===0?'→':' '} ${v} (0x${v.toString(16)})`).join(''):'<span style="color:#555">(空)</span>';
document.getElementById('stk').innerHTML=sh}
function step(){if(!prog.length)parse();exec();render()}
function run(){reset();render();let t=setInterval(()=>{if(!exec()){clearInterval(t);render();return}render()},200)}
reset();render();
</script></body></html>
🧠 底层原理全覆盖
| 计算机底层概念 | 实现方式 |
|---|---|
| 寄存器 (R0-R3) | 4个通用寄存器 + PC/SP/ZF 可视化实时变化 |
| 程序计数器 PC | 内存网格中用 青色 高亮当前指令地址 |
| 栈指针 SP | PUSH/POP 操作可视化,栈空间实时展示 |
| 标志位 ZF | CMP 比较指令设置,JE/JNE 条件跳转依赖 |
| 64字节内存 | 8×8 网格显示,十六进制地址+数据,写入高亮 |
| 指令集架构 | 自定义 ISA,支持 18条指令 |
📦 支持的指令集
算术: MOV ADD SUB MUL
逻辑: AND OR XOR NOT SHL SHR
控制流:CMP JMP JE JNE HLT
内存: PUSH POP STORE LOAD
I/O: OUT
标签: LABEL_NAME:
🎮 可以试玩的程序
斐波那契数列:
MOV R0 1
MOV R1 1
MOV R3 8
LOOP:
OUT R0
ADD R0 R1
OUT R1
ADD R1 R0
SUB R3 R1
CMP R3 0
JNE LOOP
HLT
位运算演示:
MOV R0 170
MOV R1 85
AND R2 R0
OR R2 R1
XOR R0 R1
OUT R0
SHL R0 1
OUT R0
HLT
✨ 亮点
-
🟢 单步调试:像真正的调试器一样逐条执行,观察每条指令对寄存器/内存的影响
-
🟣 内存写入高亮:PUSH/STORE 操作时,对应内存单元闪烁紫色
-
🔵 PC 追踪:程序计数器在内存网格中实时用青色标记
-
⚡ 200ms 动画运行:自动执行模式可以看到数据在寄存器间"流动"
-
📋 执行日志:每条指令的操作结果实时记录

保存为
.html直接打开,亲手写汇编、看 CPU 如何一步步执行!这是理解冯诺依曼架构最直观的方式 🎯