Files
test-repo/docs/.vitepress/theme/components/appendix/computer-fundamentals/CpuArchitectureDemo.vue
T
sanbuphy ef70b1d8e1 feat: add comprehensive backend topics and fix build issues
## 新增内容

### 附录文档扩展
- 扩展前端项目架构文档 (frontend-project-architecture.md)
- 扩展后端项目架构文档 (backend-project-architecture.md)
- 扩展数据治理文档 (data-governance.md)
- 扩展数据可视化文档 (data-visualization.md)
- 扩展分布式系统文档 (distributed-systems.md)
- 扩展高可用文档 (high-availability.md)
- 扩展单体到微服务文档 (monolith-to-microservices.md)
- 扩展系统设计方法论文档 (system-design-methodology.md)
- 扩展 Docker 容器文档 (docker-containers.md)
- 扩展 Kubernetes 文档 (kubernetes.md)
- 扩展 Linux 基础文档 (linux-basics.md)
- 扩展神经网络文档 (neural-networks.md)

### 新增交互式组件
- 数据治理组件: DataQualityDemo, DataGovernanceFrameworkDemo, DataLineageDemo
- 数据可视化组件: ChartTypeSelectorDemo, DashboardLayoutDemo
- 分布式系统组件: CAPTheoremDemo, ConsistencyModelsDemo, DistributedChallengesDemo
- 高可用组件: AvailabilityCalculatorDemo, FailoverStrategyDemo
- 系统设计组件: SystemDesignStepsDemo, CapacityEstimationDemo
- Docker 容器组件: DockerArchitectureDemo, DockerLifecycleDemo
- Kubernetes 组件: K8sArchitectureDemo, K8sWorkloadsDemo
- Linux 基础组件: LinuxFileSystemDemo, LinuxCommandDemo, LinuxPermissionsDemo
- 神经网络组件: NeuronDemo, NetworkLayersDemo, NetworkArchitectureDemo
- 单体到微服务组件: ArchEvolutionDemo
- 项目架构组件: ProjectArchitectureComparisonDemo
- 附录导航组件: AppendixFlowMap

### 英文版重构
- 将 en-us 目录重命名为 en
- 更新相关配置和组件中的语言代码

## Bug 修复
- 修复 index.js 中重复的组件导入语句导致的 build 失败
- 恢复被注释的 InvertedIndexDemo 和 SearchRelevanceDemo 导入
- 修复 HomeFeatures.vue 中 en-us 与 config.mjs 中 en 不一致导致的语言切换问题

## 其他改进
- 添加构建脚本 (scripts/build.mjs)
- 更新依赖版本
2026-02-26 04:35:28 +08:00

837 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="cpu-demo">
<div class="demo-title">CPU 指令执行周期详细演示</div>
<div class="main-layout">
<!-- LEFT: CPU internals -->
<div class="cpu-box">
<div class="cpu-label">CPU</div>
<!-- Control Unit -->
<div class="unit cu-unit" :class="{ active: isActive('CU') }">
<div class="unit-title">控制单元 CU</div>
<div class="regs-row">
<div class="reg-cell" :class="{ highlight: isHighlight('PC') }">
<span class="reg-name">PC</span>
<span class="reg-val">{{ fmt(regs.PC) }}</span>
<span class="reg-hint">程序计数器</span>
</div>
<div class="reg-cell" :class="{ highlight: isHighlight('IR') }">
<span class="reg-name">IR</span>
<span class="reg-val ir-val">{{ regs.IR || '—' }}</span>
<span class="reg-hint">指令寄存器</span>
</div>
</div>
</div>
<!-- MAR / MDR -->
<div class="unit bus-unit">
<div class="regs-row">
<div class="reg-cell" :class="{ highlight: isHighlight('MAR') }">
<span class="reg-name">MAR</span>
<span class="reg-val">{{ fmt(regs.MAR) }}</span>
<span class="reg-hint">内存地址寄存器</span>
</div>
<div class="reg-cell" :class="{ highlight: isHighlight('MDR') }">
<span class="reg-name">MDR</span>
<span class="reg-val">{{ regs.MDR !== null ? regs.MDR : '—' }}</span>
<span class="reg-hint">内存数据寄存器</span>
</div>
</div>
</div>
<!-- ALU -->
<div class="unit alu-unit" :class="{ active: isActive('ALU') }">
<div class="unit-title">算术逻辑单元 ALU</div>
<div class="regs-row">
<div class="reg-cell" :class="{ highlight: isHighlight('ACC') }">
<span class="reg-name">ACC</span>
<span class="reg-val">{{ fmt(regs.ACC) }}</span>
<span class="reg-hint">累加器</span>
</div>
<div class="alu-op" :class="{ running: isActive('ALU') }">
{{ aluOp }}
</div>
</div>
</div>
<!-- General Registers -->
<div class="unit reg-unit">
<div class="unit-title">通用寄存器组</div>
<div class="regs-row">
<div
v-for="r in ['R0','R1','R2','R3']"
:key="r"
class="reg-cell"
:class="{ highlight: isHighlight(r) }"
>
<span class="reg-name">{{ r }}</span>
<span class="reg-val">{{ fmt(regs[r]) }}</span>
</div>
</div>
</div>
</div>
<!-- CENTER: Buses -->
<div class="bus-col">
<div class="bus addr-bus" :class="{ active: busActive === 'addr' }">
<span class="bus-label">地址总线</span>
<span class="bus-val" v-if="busActive === 'addr'">{{ fmt(regs.MAR) }}</span>
</div>
<div class="bus data-bus" :class="{ active: busActive === 'data' }">
<span class="bus-label">数据总线</span>
<span class="bus-val" v-if="busActive === 'data'">{{ regs.MDR !== null ? regs.MDR : '' }}</span>
</div>
<div class="bus ctrl-bus" :class="{ active: busActive === 'ctrl' }">
<span class="bus-label">控制总线</span>
<span class="bus-val" v-if="busActive === 'ctrl'">{{ ctrlSignal }}</span>
</div>
<!-- arrows -->
<div class="arrow-row">
<div class="arrow" :class="{ lit: busActive === 'addr' }"></div>
<div class="arrow" :class="{ lit: busActive === 'data' }"></div>
<div class="arrow" :class="{ lit: busActive === 'ctrl' }"></div>
</div>
</div>
<!-- RIGHT: Memory -->
<div class="mem-box">
<div class="mem-label">主存 Memory</div>
<div class="mem-rows">
<div
v-for="(inst, i) in program"
:key="i"
class="mem-row"
:class="{
'pc-row': regs.PC === BASE_ADDR + i,
'mar-row': regs.MAR === BASE_ADDR + i && busActive === 'addr',
'fetched': fetchedAddr === BASE_ADDR + i
}"
>
<span class="pc-arrow">{{ regs.PC === BASE_ADDR + i ? '▶' : '\u00a0' }}</span>
<span class="mem-addr">{{ hex(BASE_ADDR + i) }}</span>
<span class="mem-inst">{{ inst.asm }}</span>
</div>
</div>
<div class="mem-data-area">
<div class="mem-label-sm">数据区</div>
<div
v-for="(val, addr) in dataMemory"
:key="addr"
class="mem-row data-row"
:class="{ 'mar-row': regs.MAR === addr && busActive === 'addr' }"
>
<span class="pc-arrow">&nbsp;</span>
<span class="mem-addr">{{ hex(addr) }}</span>
<span class="mem-inst">{{ val }}</span>
</div>
</div>
</div>
</div>
<!-- Pipeline bar -->
<div class="pipeline-bar">
<div
v-for="(ph, i) in phases"
:key="i"
class="ph-cell"
:class="{ 'ph-active': currentPhase === i, 'ph-done': currentPhase > i }"
>
<span class="ph-en">{{ ph.en }}</span>
<span class="ph-zh">{{ ph.zh }}</span>
</div>
</div>
<!-- Step detail -->
<div class="step-detail">
<div class="step-badge">步骤 {{ stepIndex }} / {{ totalSteps }}</div>
<div class="step-msg">{{ currentStep.msg }}</div>
<div class="step-signal" v-if="currentStep.signal">
信号<code>{{ currentStep.signal }}</code>
</div>
</div>
<!-- Controls -->
<div class="controls">
<button class="btn-clock" @click="advance" :disabled="done">
时钟脉冲 (下一步)
</button>
<button class="btn-auto" @click="toggleAuto" :disabled="done">
{{ autoRunning ? '⏸ 暂停' : '▶ 自动运行' }}
</button>
<button class="btn-reset" @click="reset"> 重置</button>
</div>
<div class="done-msg" v-if="done">
程序执行完毕共执行 {{ program.length }} 条指令{{ stepIndex }} 个时钟步骤
<button class="btn-reset inline" @click="reset">重新开始</button>
</div>
</div>
</template>
<script setup>
import { ref, computed, onUnmounted } from 'vue'
const BASE_ADDR = 0x100
const DATA_BASE = 0x200
// Program: 4 instructions
const program = [
{ asm: 'LOAD R0, [0x200]', op: 'LOAD', dst: 'R0', src: DATA_BASE },
{ asm: 'LOAD R1, #7', op: 'LOADI', dst: 'R1', imm: 7 },
{ asm: 'ADD R0, R1', op: 'ADD', dst: 'R0', src: 'R1' },
{ asm: 'STORE [0x201], R0', op: 'STORE', addr: DATA_BASE + 1, src: 'R0' },
]
const phases = [
{ en: 'Fetch', zh: '取指' },
{ en: 'Decode', zh: '译码' },
{ en: 'Execute', zh: '执行' },
{ en: 'Write Back', zh: '写回' },
]
function hex(n) { return n != null ? '0x' + n.toString(16).toUpperCase().padStart(3, '0') : '—' }
function fmt(v) { return v != null ? v : '—' }
// Build step sequence for all instructions
function buildSteps() {
const steps = []
for (let i = 0; i < program.length; i++) {
const inst = program[i]
const pc = BASE_ADDR + i
// ── FETCH (3 sub-steps) ──────────────────────────────────────────
steps.push({
phase: 0,
highlights: ['PC'],
bus: 'ctrl',
ctrlSignal: 'READ',
aluOp: '—',
regUpdates: { MAR: pc },
msg: `[取指 1/3] PC=${hex(pc)},控制单元发出读信号,将 PC 值送入 MAR(内存地址寄存器)`,
signal: `MAR ← PC (${hex(pc)})`,
})
steps.push({
phase: 0,
highlights: ['MAR'],
bus: 'addr',
ctrlSignal: 'READ',
aluOp: '—',
regUpdates: {},
msg: `[取指 2/3] MAR=${hex(pc)} 通过地址总线送到主存,主存定位该地址`,
signal: `地址总线: ${hex(pc)}`,
})
steps.push({
phase: 0,
highlights: ['MDR', 'IR'],
bus: 'data',
ctrlSignal: 'READ',
aluOp: '—',
regUpdates: { MDR: inst.asm, IR: inst.asm, PC: pc + 1 },
fetchedAddr: pc,
msg: `[取指 3/3] 主存将指令 "${inst.asm}" 经数据总线送入 MDR,再转存到 IR;PC 自增 → ${hex(pc + 1)}`,
signal: `MDR ← MEM[${hex(pc)}]IR ← MDRPC++`,
})
// ── DECODE (2 sub-steps) ─────────────────────────────────────────
steps.push({
phase: 1,
highlights: ['IR'],
bus: null,
ctrlSignal: '',
aluOp: '译码',
regUpdates: {},
msg: `[译码 1/2] 控制单元解析 IR 中的指令 "${inst.asm}",识别操作码与操作数`,
signal: `IR → 操作码: ${inst.op}`,
})
steps.push({
phase: 1,
highlights: ['CU'],
bus: 'ctrl',
ctrlSignal: inst.op,
aluOp: '准备',
regUpdates: {},
msg: `[译码 2/2] 控制单元生成控制信号 "${inst.op}",激活对应功能部件,准备操作数路径`,
signal: `控制信号: ${inst.op}`,
})
// ── EXECUTE ──────────────────────────────────────────────────────
if (inst.op === 'LOAD') {
steps.push({
phase: 2,
highlights: ['MAR'],
bus: 'addr',
ctrlSignal: 'READ',
aluOp: '读内存',
regUpdates: { MAR: inst.src },
msg: `[执行 1/2] 将操作数地址 ${hex(inst.src)} 送入 MAR,通过地址总线访问主存`,
signal: `MAR ← ${hex(inst.src)}`,
})
steps.push({
phase: 2,
highlights: ['MDR', 'R0'],
bus: 'data',
ctrlSignal: 'READ',
aluOp: '读内存',
regUpdates: { MDR: 42, [inst.dst]: 42 },
msg: `[执行 2/2] 主存数据 42 经数据总线送入 MDR,再写入目标寄存器 ${inst.dst}`,
signal: `MDR ← MEM[${hex(inst.src)}]${inst.dst} ← MDR`,
})
} else if (inst.op === 'LOADI') {
steps.push({
phase: 2,
highlights: ['IR', inst.dst],
bus: null,
ctrlSignal: 'LOADI',
aluOp: '立即数',
regUpdates: { [inst.dst]: inst.imm },
msg: `[执行] 立即数 #${inst.imm} 直接从 IR 中提取,写入寄存器 ${inst.dst}`,
signal: `${inst.dst} ← #${inst.imm}`,
})
} else if (inst.op === 'ADD') {
steps.push({
phase: 2,
highlights: ['R0', 'R1', 'ACC'],
bus: null,
ctrlSignal: 'ADD',
aluOp: 'R0 + R1',
regUpdates: { ACC: null }, // computed at runtime
msg: `[执行 1/2] ALU 读取 R0 和 R1 的值,开始加法运算`,
signal: `ALU: R0 + R1`,
})
steps.push({
phase: 2,
highlights: ['ACC'],
bus: null,
ctrlSignal: 'ADD',
aluOp: '= 结果',
regUpdates: { ACC: '__ADD_RESULT__' },
msg: `[执行 2/2] ALU 完成加法,结果暂存到累加器 ACC`,
signal: `ACC ← R0 + R1`,
})
} else if (inst.op === 'STORE') {
steps.push({
phase: 2,
highlights: ['MAR', 'MDR'],
bus: 'addr',
ctrlSignal: 'WRITE',
aluOp: '写内存',
regUpdates: { MAR: inst.addr, MDR: '__FROM_R0__' },
msg: `[执行 1/2] 将目标地址 ${hex(inst.addr)} 送入 MAR,将 ${inst.src} 的值送入 MDR,准备写入主存`,
signal: `MAR ← ${hex(inst.addr)}MDR ← ${inst.src}`,
})
steps.push({
phase: 2,
highlights: ['MDR'],
bus: 'data',
ctrlSignal: 'WRITE',
aluOp: '写内存',
regUpdates: { '__MEM__': inst.addr },
msg: `[执行 2/2] MDR 的值经数据总线写入主存地址 ${hex(inst.addr)}`,
signal: `MEM[${hex(inst.addr)}] ← MDR`,
})
}
// ── WRITE BACK ───────────────────────────────────────────────────
if (inst.op === 'ADD') {
steps.push({
phase: 3,
highlights: ['ACC', 'R0'],
bus: null,
ctrlSignal: 'WB',
aluOp: '写回',
regUpdates: { R0: '__ACC__' },
msg: `[写回 1/2] 将 ACC 中的运算结果写回目标寄存器 R0`,
signal: `R0 ← ACC`,
})
steps.push({
phase: 3,
highlights: ['PC'],
bus: null,
ctrlSignal: 'WB',
aluOp: '—',
regUpdates: {},
msg: `[写回 2/2] 写回完成,PC 已在取指阶段自增,指向下一条指令 ${hex(pc + 1)}`,
signal: `PC = ${hex(pc + 1)}`,
})
} else if (inst.op === 'STORE') {
steps.push({
phase: 3,
highlights: ['PC'],
bus: null,
ctrlSignal: 'WB',
aluOp: '—',
regUpdates: {},
msg: `[写回] STORE 指令结果已在执行阶段写入主存,写回阶段确认完成,PC=${hex(pc + 1)}`,
signal: `完成`,
})
} else {
steps.push({
phase: 3,
highlights: ['PC'],
bus: null,
ctrlSignal: 'WB',
aluOp: '—',
regUpdates: {},
msg: `[写回] 结果已写入目标寄存器,PC 已自增至 ${hex(pc + 1)},准备执行下一条指令`,
signal: `PC = ${hex(pc + 1)}`,
})
}
}
return steps
}
const allSteps = buildSteps()
const totalSteps = allSteps.length
const stepIndex = ref(0)
const done = ref(false)
const autoRunning = ref(false)
let autoTimer = null
// CPU state
const regs = ref({ PC: BASE_ADDR, IR: '', MAR: null, MDR: null, ACC: 0, R0: 0, R1: 0, R2: 0, R3: 0 })
const busActive = ref(null)
const ctrlSignal = ref('')
const aluOp = ref('—')
const fetchedAddr = ref(null)
const dataMemory = ref({ [DATA_BASE]: 42, [DATA_BASE + 1]: 0 })
const activeHighlights = ref([])
const currentPhase = ref(-1)
const currentStep = computed(() => {
if (stepIndex.value === 0) return { msg: '点击"时钟脉冲"开始逐步执行,或点击"自动运行"连续播放。', signal: null }
return allSteps[Math.min(stepIndex.value - 1, totalSteps - 1)]
})
function isHighlight(name) { return activeHighlights.value.includes(name) }
function isActive(unit) {
if (unit === 'CU') return currentPhase.value === 0 || currentPhase.value === 1
if (unit === 'ALU') return currentPhase.value === 2 && aluOp.value !== '读内存' && aluOp.value !== '写内存'
return false
}
function applyStep(step) {
currentPhase.value = step.phase
busActive.value = step.bus
ctrlSignal.value = step.ctrlSignal || ''
aluOp.value = step.aluOp || '—'
activeHighlights.value = step.highlights || []
if (step.fetchedAddr != null) fetchedAddr.value = step.fetchedAddr
for (const [k, v] of Object.entries(step.regUpdates || {})) {
if (k === '__MEM__') {
dataMemory.value = { ...dataMemory.value, [v]: regs.value.MDR }
} else if (v === '__ADD_RESULT__') {
regs.value = { ...regs.value, ACC: regs.value.R0 + regs.value.R1 }
} else if (v === '__ACC__') {
regs.value = { ...regs.value, R0: regs.value.ACC }
} else if (v === '__FROM_R0__') {
regs.value = { ...regs.value, MDR: regs.value.R0 }
} else if (v === null) {
// no-op placeholder
} else {
regs.value = { ...regs.value, [k]: v }
}
}
}
function advance() {
if (done.value) return
applyStep(allSteps[stepIndex.value])
stepIndex.value++
if (stepIndex.value >= totalSteps) {
done.value = true
stopAuto()
}
}
function toggleAuto() {
if (autoRunning.value) {
stopAuto()
} else {
autoRunning.value = true
autoTimer = setInterval(() => {
if (done.value) {
stopAuto()
return
}
advance()
}, 900)
}
}
function stopAuto() {
autoRunning.value = false
if (autoTimer) {
clearInterval(autoTimer)
autoTimer = null
}
}
function reset() {
stopAuto()
stepIndex.value = 0
done.value = false
regs.value = { PC: BASE_ADDR, IR: '', MAR: null, MDR: null, ACC: 0, R0: 0, R1: 0, R2: 0, R3: 0 }
busActive.value = null
ctrlSignal.value = ''
aluOp.value = '—'
fetchedAddr.value = null
dataMemory.value = { [DATA_BASE]: 42, [DATA_BASE + 1]: 0 }
activeHighlights.value = []
currentPhase.value = -1
}
onUnmounted(() => {
stopAuto()
})
</script>
<style scoped>
.cpu-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 10px;
background: var(--vp-c-bg-soft);
padding: 1rem 1.2rem;
margin: 1rem 0;
font-size: 0.82rem;
}
.demo-title {
font-weight: 700;
font-size: 0.9rem;
color: var(--vp-c-brand-1);
margin-bottom: 0.9rem;
text-align: center;
}
/* ── Main layout ── */
.main-layout {
display: grid;
grid-template-columns: 1fr 80px 1fr;
gap: 0.6rem;
margin-bottom: 0.8rem;
}
/* ── CPU box ── */
.cpu-box {
border: 2px dashed var(--vp-c-brand-1);
border-radius: 8px;
padding: 0.6rem;
background: var(--vp-c-bg);
position: relative;
}
.cpu-label {
position: absolute;
top: -0.6rem;
left: 0.8rem;
background: var(--vp-c-bg-soft);
padding: 0 0.4rem;
font-weight: 700;
font-size: 0.75rem;
color: var(--vp-c-brand-1);
}
.unit {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.45rem 0.5rem;
margin-bottom: 0.45rem;
background: var(--vp-c-bg-soft);
transition: background 0.25s, border-color 0.25s;
}
.unit:last-child { margin-bottom: 0; }
.unit.active {
background: var(--vp-c-brand-soft);
border-color: var(--vp-c-brand-1);
}
.unit-title {
font-size: 0.72rem;
font-weight: 700;
color: var(--vp-c-text-2);
margin-bottom: 0.35rem;
}
.regs-row {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
}
.reg-cell {
display: flex;
flex-direction: column;
align-items: center;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
padding: 0.25rem 0.4rem;
min-width: 52px;
transition: background 0.2s, border-color 0.2s;
}
.reg-cell.highlight {
background: #fef08a;
border-color: #ca8a04;
}
.dark .reg-cell.highlight {
background: #713f12;
border-color: #fbbf24;
}
.reg-name {
font-size: 0.65rem;
font-weight: 700;
color: var(--vp-c-brand-1);
}
.reg-val {
font-family: monospace;
font-size: 0.72rem;
font-weight: 600;
color: var(--vp-c-text-1);
word-break: break-all;
text-align: center;
}
.ir-val {
font-size: 0.6rem;
max-width: 90px;
}
.reg-hint {
font-size: 0.55rem;
color: var(--vp-c-text-3);
text-align: center;
}
.alu-op {
display: flex;
align-items: center;
justify-content: center;
font-family: monospace;
font-size: 0.72rem;
font-weight: 700;
color: var(--vp-c-text-2);
background: var(--vp-c-bg);
border: 1px dashed var(--vp-c-divider);
border-radius: 4px;
padding: 0.25rem 0.5rem;
min-width: 60px;
transition: color 0.2s;
}
.alu-op.running {
color: var(--vp-c-brand-1);
border-color: var(--vp-c-brand-1);
}
/* ── Bus column ── */
.bus-col {
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.5rem;
}
.bus {
border-radius: 4px;
padding: 0.3rem 0.4rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
display: flex;
flex-direction: column;
align-items: center;
transition: background 0.25s, border-color 0.25s;
}
.bus.active { border-color: var(--vp-c-brand-1); background: var(--vp-c-brand-soft); }
.addr-bus.active { border-color: #3b82f6; background: #eff6ff; }
.data-bus.active { border-color: #10b981; background: #ecfdf5; }
.ctrl-bus.active { border-color: #f59e0b; background: #fffbeb; }
.dark .addr-bus.active { background: #1e3a5f; }
.dark .data-bus.active { background: #064e3b; }
.dark .ctrl-bus.active { background: #451a03; }
.bus-label {
font-size: 0.6rem;
font-weight: 700;
color: var(--vp-c-text-3);
writing-mode: vertical-rl;
text-orientation: mixed;
letter-spacing: 1px;
}
.bus-val {
font-family: monospace;
font-size: 0.6rem;
color: var(--vp-c-brand-1);
word-break: break-all;
text-align: center;
margin-top: 0.2rem;
}
.arrow-row {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.arrow {
font-size: 1rem;
color: var(--vp-c-text-3);
transition: color 0.2s;
}
.arrow.lit { color: var(--vp-c-brand-1); }
/* ── Memory box ── */
.mem-box {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0.6rem;
background: var(--vp-c-bg-alt);
}
.mem-label {
font-weight: 700;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-bottom: 0.4rem;
text-align: center;
}
.mem-label-sm {
font-size: 0.65rem;
color: var(--vp-c-text-3);
margin: 0.4rem 0 0.2rem;
border-top: 1px dashed var(--vp-c-divider);
padding-top: 0.3rem;
}
.mem-rows { display: flex; flex-direction: column; gap: 0.25rem; }
.mem-row {
display: flex;
align-items: center;
gap: 0.3rem;
font-family: monospace;
font-size: 0.7rem;
padding: 0.2rem 0.3rem;
border-radius: 3px;
border: 1px solid transparent;
transition: background 0.2s, border-color 0.2s;
}
.mem-row.pc-row {
background: var(--vp-c-brand-soft);
border-color: var(--vp-c-brand-1);
}
.mem-row.mar-row {
background: #eff6ff;
border-color: #3b82f6;
}
.dark .mem-row.mar-row { background: #1e3a5f; }
.mem-row.fetched {
background: #f0fdf4;
border-color: #10b981;
}
.dark .mem-row.fetched { background: #064e3b; }
.pc-arrow { color: var(--vp-c-brand-1); font-weight: 700; width: 10px; }
.mem-addr { color: var(--vp-c-text-3); min-width: 42px; }
.mem-inst { color: var(--vp-c-text-1); }
.data-row .mem-inst { color: var(--vp-c-text-2); }
/* ── Pipeline bar ── */
.pipeline-bar {
display: flex;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
margin-bottom: 0.7rem;
}
.ph-cell {
flex: 1;
text-align: center;
padding: 0.35rem 0;
border-right: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
transition: background 0.2s;
}
.ph-cell:last-child { border-right: none; }
.ph-cell.ph-done { background: var(--vp-c-bg-alt); }
.ph-cell.ph-active { background: var(--vp-c-brand-1); color: white; }
.ph-en { display: block; font-size: 0.65rem; font-weight: 700; }
.ph-zh { display: block; font-size: 0.72rem; }
/* ── Step detail ── */
.step-detail {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.6rem 0.8rem;
margin-bottom: 0.7rem;
min-height: 60px;
}
.step-badge {
display: inline-block;
font-size: 0.65rem;
font-weight: 700;
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
border-radius: 3px;
padding: 0.1rem 0.4rem;
margin-bottom: 0.3rem;
}
.step-msg {
font-size: 0.8rem;
color: var(--vp-c-text-1);
line-height: 1.5;
}
.step-signal {
margin-top: 0.3rem;
font-size: 0.72rem;
color: var(--vp-c-text-2);
}
.step-signal code {
background: var(--vp-c-bg-soft);
padding: 0.1rem 0.3rem;
border-radius: 3px;
font-family: monospace;
}
/* ── Controls ── */
.controls {
display: flex;
gap: 0.6rem;
justify-content: center;
flex-wrap: wrap;
}
.btn-clock, .btn-auto, .btn-reset {
padding: 0.45rem 1rem;
border-radius: 5px;
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
border: none;
transition: opacity 0.2s;
}
.btn-clock { background: var(--vp-c-brand-1); color: white; }
.btn-auto { background: #10b981; color: white; }
.btn-reset { background: transparent; border: 1px solid var(--vp-c-divider); color: var(--vp-c-text-2); }
.btn-clock:disabled, .btn-auto:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-clock:not(:disabled):hover { opacity: 0.85; }
.btn-auto:not(:disabled):hover { opacity: 0.85; }
.done-msg {
margin-top: 0.7rem;
text-align: center;
font-size: 0.82rem;
color: #10b981;
font-weight: 600;
}
.btn-reset.inline {
margin-left: 0.5rem;
padding: 0.2rem 0.6rem;
font-size: 0.75rem;
}
@media (max-width: 680px) {
.main-layout {
grid-template-columns: 1fr;
}
.bus-col {
flex-direction: row;
justify-content: space-around;
}
.bus-label { writing-mode: horizontal-tb; }
.arrow-row { flex-direction: row; }
}
</style>