Refactor levels to YAML, remove icons, add vite-plugin-yaml
This commit is contained in:
parent
bab0bb0c6b
commit
247cf85e57
125
frontend/package-lock.json
generated
125
frontend/package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "simpleasm",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
@ -66,7 +67,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
@ -82,7 +82,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@ -98,7 +97,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@ -114,7 +112,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@ -130,7 +127,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -146,7 +142,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -162,7 +157,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@ -178,7 +172,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@ -194,7 +187,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -210,7 +202,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -226,7 +217,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -242,7 +232,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -258,7 +247,6 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -274,7 +262,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -290,7 +277,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -306,7 +292,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -322,7 +307,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -338,7 +322,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
@ -354,7 +337,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
@ -370,7 +352,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
@ -386,7 +367,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -402,7 +382,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -418,7 +397,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -432,6 +410,40 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||
},
|
||||
"node_modules/@modyfi/vite-plugin-yaml": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.1.tgz",
|
||||
"integrity": "sha512-rEbfFNlMGLKpAYs2RsfLAhxCHFa6M4QKHHk0A4EYcCJAUwFtFO6qiEdLjUGUTtnRUxAC7GxxCa+ZbeUILSDvqQ==",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "5.1.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"tosource": "2.0.0-alpha.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">=3.2.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
|
||||
"integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
|
||||
@ -439,7 +451,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@ -452,7 +463,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@ -465,7 +475,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -478,7 +487,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -491,7 +499,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@ -504,7 +511,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@ -517,7 +523,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -530,7 +535,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -543,7 +547,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -556,7 +559,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -569,7 +571,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -582,7 +583,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -595,7 +595,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -608,7 +607,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -621,7 +619,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -634,7 +631,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -647,7 +643,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -660,7 +655,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -673,7 +667,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -686,7 +679,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
@ -699,7 +691,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
@ -712,7 +703,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -725,7 +715,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -738,7 +727,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -751,7 +739,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -760,8 +747,7 @@
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
@ -872,6 +858,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz",
|
||||
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg=="
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
@ -892,7 +883,6 @@
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
@ -935,7 +925,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -945,6 +934,17 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@ -975,6 +975,17 @@
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz",
|
||||
@ -1027,7 +1038,6 @@
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
|
||||
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@ -1075,11 +1085,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tosource": {
|
||||
"version": "2.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.21",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="card">
|
||||
<div class="badge">🎉</div>
|
||||
<h2>恭喜通关!</h2>
|
||||
<p class="lname">{{ level.icon }} {{ level.title }}</p>
|
||||
<p class="lname">{{ level.title }}</p>
|
||||
|
||||
<div class="stars-row">
|
||||
<span v-for="s in 3" :key="s"
|
||||
|
||||
@ -1,376 +1,5 @@
|
||||
export const levels = [
|
||||
// ===== Level 1: 认识寄存器 =====
|
||||
{
|
||||
id: 1,
|
||||
title: '认识寄存器',
|
||||
subtitle: '小机器人的记忆槽',
|
||||
icon: '🤖',
|
||||
description: '学习 MOV 指令给寄存器赋值',
|
||||
tutorial: [
|
||||
{
|
||||
title: '什么是寄存器?',
|
||||
text: 'CPU 是计算机的大脑,而**寄存器**是它手边的小抽屉 —— 速度最快的存储空间!我们的机器有 8 个寄存器:**R0** 到 **R7**。',
|
||||
},
|
||||
{
|
||||
title: 'MOV 指令',
|
||||
text: '`MOV` 把一个数字放进寄存器。注意数字前面要加 **#** 号,表示"这是一个数值":',
|
||||
code: 'MOV R0, #42 ; 把 42 放进 R0\nMOV R1, #100 ; 把 100 放进 R1',
|
||||
},
|
||||
{
|
||||
title: 'HLT 指令',
|
||||
text: '程序最后要写 `HLT`(halt = 停止),告诉机器"运行结束!"',
|
||||
code: 'MOV R0, #42\nHLT',
|
||||
},
|
||||
],
|
||||
goal: '把数字 **42** 放进 **R0** 寄存器',
|
||||
initialState: {},
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 42 } } }],
|
||||
hints: [
|
||||
'MOV 的格式:MOV 寄存器, #数字',
|
||||
'试试:MOV R0, #???',
|
||||
'答案:MOV R0, #42 然后 HLT',
|
||||
],
|
||||
starThresholds: [2, 3, 5],
|
||||
starterCode: '; 把 42 放进 R0 寄存器\n; 提示:数字前面要加 # 号\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
const modules = import.meta.glob('./levels/*.yaml', { eager: true })
|
||||
|
||||
// ===== Level 2: 数据搬运工 =====
|
||||
{
|
||||
id: 2,
|
||||
title: '数据搬运工',
|
||||
subtitle: '寄存器之间的复制',
|
||||
icon: '📦',
|
||||
description: '学习在寄存器之间复制数据',
|
||||
tutorial: [
|
||||
{
|
||||
title: '寄存器间复制',
|
||||
text: 'MOV 也能把一个寄存器的值**复制**到另一个(这时不需要 # 号):',
|
||||
code: 'MOV R1, R0 ; 把 R0 的值复制到 R1',
|
||||
},
|
||||
{
|
||||
title: '复制,不是移动!',
|
||||
text: '虽然叫 "MOV"(移动),但其实是**复制**。执行后 R0 的值不变,R1 变成和 R0 一样。',
|
||||
},
|
||||
],
|
||||
goal: 'R0 已经有值 **7**,把它复制到 **R1** 和 **R2**',
|
||||
initialState: { registers: { R0: 7 } },
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 7, R1: 7, R2: 7 } } }],
|
||||
hints: [
|
||||
'MOV 寄存器, 寄存器 —— 把右边复制到左边',
|
||||
'MOV R1, R0 可以把 R0 复制到 R1',
|
||||
'答案:MOV R1, R0 / MOV R2, R0 / HLT',
|
||||
],
|
||||
starThresholds: [3, 4, 6],
|
||||
starterCode: '; R0 = 7\n; 把 R0 复制到 R1 和 R2\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 3: 加减法 =====
|
||||
{
|
||||
id: 3,
|
||||
title: '加减法',
|
||||
subtitle: '三操作数的威力',
|
||||
icon: '➕',
|
||||
description: '学习 ADD 和 SUB 指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: 'ADD —— 加法(三操作数)',
|
||||
text: 'ARM 风格的加法很酷:**三个操作数**!第一个放结果,后两个是被运算的值:',
|
||||
code: 'ADD R2, R0, R1 ; R2 = R0 + R1\nADD R0, R0, #10 ; R0 = R0 + 10',
|
||||
},
|
||||
{
|
||||
title: 'SUB —— 减法',
|
||||
text: 'SUB 同理,也是三操作数:',
|
||||
code: 'SUB R2, R0, R1 ; R2 = R0 - R1\nSUB R0, R0, #5 ; R0 = R0 - 5',
|
||||
},
|
||||
{
|
||||
title: '好处',
|
||||
text: '三操作数的好处:可以直接把结果放到新的寄存器,**不用先复制**!',
|
||||
},
|
||||
],
|
||||
goal: 'R0=**15**,R1=**27**,计算 R0+R1 存入 **R2**(R0和R1不变)',
|
||||
initialState: { registers: { R0: 15, R1: 27 } },
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 15, R1: 27, R2: 42 } } }],
|
||||
hints: [
|
||||
'ADD 第一个参数放结果,后两个参数相加',
|
||||
'ADD R2, R0, R1 —— 结果存入 R2',
|
||||
'答案:ADD R2, R0, R1 / HLT',
|
||||
],
|
||||
starThresholds: [2, 3, 5],
|
||||
starterCode: '; R0=15, R1=27\n; 计算 R0 + R1,结果存入 R2\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 4: 乘法与除法 =====
|
||||
{
|
||||
id: 4,
|
||||
title: '乘法与除法',
|
||||
subtitle: '更强的算术能力',
|
||||
icon: '✖️',
|
||||
description: '学习 MUL 和 DIV 指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: 'MUL —— 乘法',
|
||||
text: 'MUL 也是三操作数:',
|
||||
code: 'MOV R0, #6\nMOV R1, #7\nMUL R2, R0, R1 ; R2 = 6 × 7 = 42',
|
||||
},
|
||||
{
|
||||
title: 'DIV —— 除法(取整)',
|
||||
text: 'DIV 做整数除法(只留整数部分):',
|
||||
code: 'MOV R0, #100\nMOV R1, #4\nDIV R2, R0, R1 ; R2 = 100 ÷ 4 = 25',
|
||||
},
|
||||
],
|
||||
goal: '计算 **6 × 7** 存入 R0,**100 ÷ 4** 存入 R1',
|
||||
initialState: {},
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 42, R1: 25 } } }],
|
||||
hints: [
|
||||
'先 MOV 数字到寄存器,再 MUL/DIV',
|
||||
'MUL R0, R2, R3 可以把 R2×R3 的结果放到 R0',
|
||||
'答案:MOV R2, #6 / MOV R3, #7 / MUL R0, R2, R3 / MOV R2, #100 / MOV R3, #4 / DIV R1, R2, R3 / HLT',
|
||||
],
|
||||
starThresholds: [7, 9, 12],
|
||||
starterCode: '; 计算 6×7 存入 R0\n; 计算 100÷4 存入 R1\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 5: 位运算魔法 =====
|
||||
{
|
||||
id: 5,
|
||||
title: '位运算魔法',
|
||||
subtitle: '0和1的秘密',
|
||||
icon: '🔮',
|
||||
description: '学习 AND、ORR、EOR、MVN 指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: '二进制世界',
|
||||
text: '计算机内部用 **0** 和 **1** 存储一切。42 的二进制是 `00101010`。右边面板会显示每个寄存器的二进制值!',
|
||||
},
|
||||
{
|
||||
title: 'AND —— 都是1才是1',
|
||||
text: 'AND 逐位比较,两个都是 1 结果才是 1。可以用来"提取"某些位:',
|
||||
code: '; 11111111 (255)\n; AND 00001111 (15)\n; = 00001111 (15)\nAND R0, R0, #15',
|
||||
},
|
||||
{
|
||||
title: '其他位运算',
|
||||
text: '**ORR** = 有一个1就是1 (OR)\n**EOR** = 不同才是1 (XOR)\n**MVN** = 全部翻转 (NOT)',
|
||||
code: 'ORR R0, R0, #240 ; 设置高4位\nEOR R0, R0, #255 ; 翻转低8位\nMVN R0, R0 ; 翻转所有位',
|
||||
},
|
||||
],
|
||||
goal: 'R0 = **255** (二进制 11111111),用 AND 提取**低4位**,使 R0 变成 **15**',
|
||||
initialState: { registers: { R0: 255 } },
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 15 } } }],
|
||||
hints: [
|
||||
'AND 用来保留某些位,把其他位清零',
|
||||
'低4位的掩码是 15(二进制 00001111)',
|
||||
'答案:AND R0, R0, #15 / HLT',
|
||||
],
|
||||
starThresholds: [2, 3, 5],
|
||||
starterCode: '; R0 = 255 (二进制 11111111)\n; 用 AND 提取低4位\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 6: 移位操作 =====
|
||||
{
|
||||
id: 6,
|
||||
title: '移位操作',
|
||||
subtitle: '位的舞蹈',
|
||||
icon: '↔️',
|
||||
description: '学习 LSL 和 LSR 指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: 'LSL —— 逻辑左移',
|
||||
text: '所有位向左移,右边补0。**左移1位 = 乘以2**,左移3位 = 乘以8:',
|
||||
code: '; 5 = 00000101\nLSL R0, R0, #1 ; 00001010 = 10 (×2)\nLSL R0, R0, #1 ; 00010100 = 20 (×2)',
|
||||
},
|
||||
{
|
||||
title: 'LSR —— 逻辑右移',
|
||||
text: '所有位向右移,左边补0。**右移1位 = 除以2**:',
|
||||
code: 'MOV R0, #40\nLSR R0, R0, #1 ; 20 (÷2)\nLSR R0, R0, #2 ; 5 (÷4)',
|
||||
},
|
||||
{
|
||||
title: '程序员的技巧',
|
||||
text: '在真实的 ARM 处理器中,移位比乘除快得多!`LSL R0, R0, #3` 比 `MUL R0, R0, #8` 高效。',
|
||||
},
|
||||
],
|
||||
goal: 'R0 = **5**,只用**移位操作**把它变成 **40**(40 = 5 × 8 = 5 × 2³)',
|
||||
initialState: { registers: { R0: 5 } },
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 40 } } }],
|
||||
hints: [
|
||||
'8 = 2³,乘以8就是左移3位',
|
||||
'LSL R0, R0, #3',
|
||||
'就这一条指令!',
|
||||
],
|
||||
starThresholds: [2, 3, 5],
|
||||
blockedOps: ['MUL', 'DIV'],
|
||||
starterCode: '; R0 = 5\n; 用 LSL 让 R0 变成 40(不能用 MUL)\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 7: 内存读写 =====
|
||||
{
|
||||
id: 7,
|
||||
title: '内存读写',
|
||||
subtitle: '打开更大的空间',
|
||||
icon: '💾',
|
||||
description: '学习 LDR 和 STR 指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: '什么是内存?',
|
||||
text: '寄存器只有8个,太少了!**内存**像一排256格的柜子,每格有编号(0-255)。',
|
||||
},
|
||||
{
|
||||
title: 'LDR —— 从内存读取',
|
||||
text: '先把地址放进寄存器,再用 `LDR` 从那个地址读数据:',
|
||||
code: 'MOV R1, #0 ; 地址 = 0\nLDR R0, [R1] ; R0 = 内存[0]',
|
||||
},
|
||||
{
|
||||
title: 'STR —— 写入内存',
|
||||
text: '`STR` 把寄存器的值写到内存:',
|
||||
code: 'MOV R1, #5 ; 地址 = 5\nSTR R0, [R1] ; 内存[5] = R0',
|
||||
},
|
||||
{
|
||||
title: '偏移寻址',
|
||||
text: '还可以加偏移量:`[R1, #4]` 表示地址 R1+4:',
|
||||
code: 'MOV R1, #0\nLDR R0, [R1, #0] ; 内存[0]\nLDR R2, [R1, #1] ; 内存[1]',
|
||||
},
|
||||
],
|
||||
goal: '内存[0]=**10**,内存[1]=**20**,计算它们的和存入 **内存[2]**',
|
||||
initialState: { memory: { 0: 10, 1: 20 } },
|
||||
testCases: [{ init: {}, expected: { memory: { 2: 30 } } }],
|
||||
hints: [
|
||||
'先用 LDR 把内存值读到寄存器,算完用 STR 写回',
|
||||
'MOV R3, #0 设基地址,LDR R0, [R3, #0] 读第一个值',
|
||||
'答案:MOV R3, #0 / LDR R0, [R3, #0] / LDR R1, [R3, #1] / ADD R2, R0, R1 / STR R2, [R3, #2] / HLT',
|
||||
],
|
||||
starThresholds: [6, 8, 10],
|
||||
starterCode: '; 内存[0]=10, 内存[1]=20\n; 计算它们的和,存入内存[2]\n;\n; 提示:先 MOV 一个地址到寄存器\n; 然后用 LDR/STR 读写内存\n\n\nHLT',
|
||||
showMemory: true,
|
||||
memoryRange: [0, 15],
|
||||
},
|
||||
|
||||
// ===== Level 8: 比较与跳转 =====
|
||||
{
|
||||
id: 8,
|
||||
title: '比较与跳转',
|
||||
subtitle: '让程序会做决定',
|
||||
icon: '🔀',
|
||||
description: '学习 CMP 和条件分支指令',
|
||||
tutorial: [
|
||||
{
|
||||
title: '到目前为止...',
|
||||
text: '程序都是从头到尾顺序执行。但有了**分支**,程序就能做决定了!',
|
||||
},
|
||||
{
|
||||
title: 'CMP —— 比较',
|
||||
text: '`CMP` 比较两个值,记住比较结果(不会改变它们的值):',
|
||||
code: 'CMP R0, #10 ; 比较 R0 和 10',
|
||||
},
|
||||
{
|
||||
title: '条件分支',
|
||||
text: '比较后用 **B** (Branch=分支) 跳转:',
|
||||
code: 'BEQ label ; 等于则跳(Equal)\nBNE label ; 不等则跳(Not Equal)\nBGT label ; 大于则跳(Greater Than)\nBLT label ; 小于则跳(Less Than)\nB label ; 无条件跳',
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
text: '**标签**是代码里的记号,分支指令跳到标签位置。标签后面加冒号:',
|
||||
code: 'CMP R0, #10\nBGT big\nMOV R1, #0 ; R0 <= 10\nB done ; 跳过下面\nbig:\nMOV R1, #1 ; R0 > 10\ndone:\nHLT',
|
||||
},
|
||||
],
|
||||
goal: 'R0=**15**。如果 R0 > 10 则 R1 = **1**;否则 R1 = **0**',
|
||||
initialState: { registers: { R0: 15 } },
|
||||
testCases: [
|
||||
{ init: { registers: { R0: 15 } }, expected: { registers: { R1: 1 } } },
|
||||
{ init: { registers: { R0: 5 } }, expected: { registers: { R1: 0 } } },
|
||||
{ init: { registers: { R0: 10 } }, expected: { registers: { R1: 0 } } },
|
||||
],
|
||||
hints: [
|
||||
'先设 R1=#0(默认),再比较 R0 和 10',
|
||||
'如果 R0 > 10,跳到标签把 R1 改成 1',
|
||||
'答案:MOV R1, #0 / CMP R0, #10 / BLE done / MOV R1, #1 / done: HLT',
|
||||
],
|
||||
starThresholds: [5, 7, 9],
|
||||
starterCode: '; 如果 R0 > 10,则 R1 = 1\n; 否则 R1 = 0\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 9: 循环 =====
|
||||
{
|
||||
id: 9,
|
||||
title: '循环',
|
||||
subtitle: '重复的力量',
|
||||
icon: '🔄',
|
||||
description: '用分支指令创建循环',
|
||||
tutorial: [
|
||||
{
|
||||
title: '什么是循环?',
|
||||
text: '循环让一段代码**反复执行**。在汇编中,循环就是**跳回前面的标签**!',
|
||||
},
|
||||
{
|
||||
title: '循环结构',
|
||||
text: '①初始化 ②做事 ③更新计数器 ④判断+跳回:',
|
||||
code: 'MOV R4, #0 ; ① 初始化\nloop: ; 循环开始\n ADD R4, R4, #1 ; ②③ 计数+1\n CMP R4, #5 ; ④ 到5了吗?\n BLE loop ; 没到就跳回\nHLT',
|
||||
},
|
||||
{
|
||||
title: '注意!',
|
||||
text: '忘了更新计数器 = **死循环**(别担心,超过10000步会自动停止)。',
|
||||
},
|
||||
],
|
||||
goal: '计算 **1+2+3+...+10** 的和存入 **R0**(答案是55)',
|
||||
initialState: {},
|
||||
testCases: [{ init: {}, expected: { registers: { R0: 55 } } }],
|
||||
hints: [
|
||||
'R0 累加结果,R4 做计数器(1到10)',
|
||||
'循环体:ADD R0, R0, R4 / ADD R4, R4, #1 / CMP R4, #10 / BLE loop',
|
||||
'完整:MOV R0, #0 / MOV R4, #1 / loop: ADD R0, R0, R4 / ADD R4, R4, #1 / CMP R4, #10 / BLE loop / HLT',
|
||||
],
|
||||
starThresholds: [7, 9, 12],
|
||||
starterCode: '; 计算 1+2+3+...+10\n; 结果存入 R0\n;\n; 提示:用一个寄存器做计数器\n\n\nHLT',
|
||||
showMemory: false,
|
||||
},
|
||||
|
||||
// ===== Level 10: 终极挑战 =====
|
||||
{
|
||||
id: 10,
|
||||
title: '终极挑战',
|
||||
subtitle: '寻找最大值',
|
||||
icon: '🏆',
|
||||
description: '综合运用所有技能!',
|
||||
tutorial: [
|
||||
{
|
||||
title: '最后一关!',
|
||||
text: '你已经学会了寄存器、算术、位运算、内存、分支和循环。现在把**所有技能**结合起来!',
|
||||
},
|
||||
{
|
||||
title: '挑战说明',
|
||||
text: '内存地址 0-4 存了5个数字。你要找到**最大值**和它的**位置**。需要:循环 + 内存读取 + 比较分支。',
|
||||
},
|
||||
{
|
||||
title: '解题思路',
|
||||
text: '1. 假设第一个数最大(R0=内存[0],R1=位置0)\n2. 循环检查剩余的数\n3. 如果发现更大的,更新最大值和位置\n4. 直到检查完全部5个数',
|
||||
code: '; 伪代码:\n; R0 = max = mem[0]\n; R1 = maxIdx = 0\n; for R4 = 1 to 4:\n; R5 = mem[R4]\n; if R5 > R0: R0=R5, R1=R4',
|
||||
},
|
||||
],
|
||||
goal: '内存[0..4] 有5个数,找出**最大值**存入 **R0**,其**位置**存入 **R1**',
|
||||
initialState: { memory: { 0: 5, 1: 3, 2: 8, 3: 1, 4: 7 } },
|
||||
testCases: [
|
||||
{
|
||||
init: { memory: { 0: 5, 1: 3, 2: 8, 3: 1, 4: 7 } },
|
||||
expected: { registers: { R0: 8, R1: 2 } },
|
||||
},
|
||||
{
|
||||
init: { memory: { 0: 1, 1: 9, 2: 4, 3: 9, 4: 2 } },
|
||||
expected: { registers: { R0: 9, R1: 1 } },
|
||||
},
|
||||
],
|
||||
hints: [
|
||||
'R0=最大值, R1=位置, R4=循环计数器, R5=当前值, R3=基地址',
|
||||
'用 LDR R5, [R3, R4] 不行的话,可以用 R3 当地址:MOV R3, R4 / LDR R5, [R3]',
|
||||
'循环体:把 R4 当地址读内存 → CMP R5, R0 → BLE skip → 更新 R0,R1 → skip: ADD R4, R4, #1 → CMP R4, #5 → BLT loop',
|
||||
],
|
||||
starThresholds: [12, 15, 20],
|
||||
starterCode: '; 内存[0..4] = [5, 3, 8, 1, 7]\n; 找最大值存入 R0,位置存入 R1\n;\n; 提示:用 R4 做循环变量\n; 用 MOV + LDR 读取内存\n\n\nHLT',
|
||||
showMemory: true,
|
||||
memoryRange: [0, 15],
|
||||
},
|
||||
]
|
||||
export const levels = Object.values(modules)
|
||||
.map(m => m.default)
|
||||
.sort((a, b) => a.id - b.id)
|
||||
|
||||
48
frontend/src/lib/levels/01.yaml
Normal file
48
frontend/src/lib/levels/01.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
id: 1
|
||||
title: 认识寄存器
|
||||
subtitle: 小机器人的记忆槽
|
||||
description: 学习 MOV 指令给寄存器赋值
|
||||
|
||||
tutorial:
|
||||
- title: 什么是寄存器?
|
||||
text: >
|
||||
CPU 是计算机的大脑,而**寄存器**是它手边的小抽屉 ——
|
||||
速度最快的存储空间!我们的机器有 8 个寄存器:**R0** 到 **R7**。
|
||||
- title: MOV 指令
|
||||
text: >
|
||||
`MOV` 把一个数字放进寄存器。注意数字前面要加 **#** 号,表示"这是一个数值":
|
||||
code: |
|
||||
MOV R0, #42 ; 把 42 放进 R0
|
||||
MOV R1, #100 ; 把 100 放进 R1
|
||||
- title: HLT 指令
|
||||
text: >
|
||||
程序最后要写 `HLT`(halt = 停止),告诉机器"运行结束!"
|
||||
code: |
|
||||
MOV R0, #42
|
||||
HLT
|
||||
|
||||
goal: 把数字 **42** 放进 **R0** 寄存器
|
||||
|
||||
initialState: {}
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 42
|
||||
|
||||
hints:
|
||||
- "MOV 的格式:MOV 寄存器, #数字"
|
||||
- "试试:MOV R0, #???"
|
||||
- "答案:MOV R0, #42 然后 HLT"
|
||||
|
||||
starThresholds: [2, 3, 5]
|
||||
|
||||
starterCode: |
|
||||
; 把 42 放进 R0 寄存器
|
||||
; 提示:数字前面要加 # 号
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
44
frontend/src/lib/levels/02.yaml
Normal file
44
frontend/src/lib/levels/02.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
id: 2
|
||||
title: 数据搬运工
|
||||
subtitle: 寄存器之间的复制
|
||||
description: 学习在寄存器之间复制数据
|
||||
|
||||
tutorial:
|
||||
- title: 寄存器间复制
|
||||
text: >
|
||||
MOV 也能把一个寄存器的值**复制**到另一个(这时不需要 # 号):
|
||||
code: |
|
||||
MOV R1, R0 ; 把 R0 的值复制到 R1
|
||||
- title: 复制,不是移动!
|
||||
text: >
|
||||
虽然叫 "MOV"(移动),但其实是**复制**。执行后 R0 的值不变,R1 变成和 R0 一样。
|
||||
|
||||
goal: R0 已经有值 **7**,把它复制到 **R1** 和 **R2**
|
||||
|
||||
initialState:
|
||||
registers:
|
||||
R0: 7
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 7
|
||||
R1: 7
|
||||
R2: 7
|
||||
|
||||
hints:
|
||||
- "MOV 寄存器, 寄存器 —— 把右边复制到左边"
|
||||
- "MOV R1, R0 可以把 R0 复制到 R1"
|
||||
- "答案:MOV R1, R0 / MOV R2, R0 / HLT"
|
||||
|
||||
starThresholds: [3, 4, 6]
|
||||
|
||||
starterCode: |
|
||||
; R0 = 7
|
||||
; 把 R0 复制到 R1 和 R2
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
52
frontend/src/lib/levels/03.yaml
Normal file
52
frontend/src/lib/levels/03.yaml
Normal file
@ -0,0 +1,52 @@
|
||||
id: 3
|
||||
title: 加减法
|
||||
subtitle: 三操作数的威力
|
||||
description: 学习 ADD 和 SUB 指令
|
||||
|
||||
tutorial:
|
||||
- title: ADD —— 加法(三操作数)
|
||||
text: >
|
||||
ARM 风格的加法很酷:**三个操作数**!第一个放结果,后两个是被运算的值:
|
||||
code: |
|
||||
ADD R2, R0, R1 ; R2 = R0 + R1
|
||||
ADD R0, R0, #10 ; R0 = R0 + 10
|
||||
- title: SUB —— 减法
|
||||
text: >
|
||||
SUB 同理,也是三操作数:
|
||||
code: |
|
||||
SUB R2, R0, R1 ; R2 = R0 - R1
|
||||
SUB R0, R0, #5 ; R0 = R0 - 5
|
||||
- title: 好处
|
||||
text: >
|
||||
三操作数的好处:可以直接把结果放到新的寄存器,**不用先复制**!
|
||||
|
||||
goal: R0=**15**,R1=**27**,计算 R0+R1 存入 **R2**(R0和R1不变)
|
||||
|
||||
initialState:
|
||||
registers:
|
||||
R0: 15
|
||||
R1: 27
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 15
|
||||
R1: 27
|
||||
R2: 42
|
||||
|
||||
hints:
|
||||
- "ADD 第一个参数放结果,后两个参数相加"
|
||||
- "ADD R2, R0, R1 —— 结果存入 R2"
|
||||
- "答案:ADD R2, R0, R1 / HLT"
|
||||
|
||||
starThresholds: [2, 3, 5]
|
||||
|
||||
starterCode: |
|
||||
; R0=15, R1=27
|
||||
; 计算 R0 + R1,结果存入 R2
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
47
frontend/src/lib/levels/04.yaml
Normal file
47
frontend/src/lib/levels/04.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
id: 4
|
||||
title: 乘法与除法
|
||||
subtitle: 更强的算术能力
|
||||
description: 学习 MUL 和 DIV 指令
|
||||
|
||||
tutorial:
|
||||
- title: MUL —— 乘法
|
||||
text: >
|
||||
MUL 也是三操作数:
|
||||
code: |
|
||||
MOV R0, #6
|
||||
MOV R1, #7
|
||||
MUL R2, R0, R1 ; R2 = 6 × 7 = 42
|
||||
- title: DIV —— 除法(取整)
|
||||
text: >
|
||||
DIV 做整数除法(只留整数部分):
|
||||
code: |
|
||||
MOV R0, #100
|
||||
MOV R1, #4
|
||||
DIV R2, R0, R1 ; R2 = 100 ÷ 4 = 25
|
||||
|
||||
goal: 计算 **6 × 7** 存入 R0,**100 ÷ 4** 存入 R1
|
||||
|
||||
initialState: {}
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 42
|
||||
R1: 25
|
||||
|
||||
hints:
|
||||
- "先 MOV 数字到寄存器,再 MUL/DIV"
|
||||
- "MUL R0, R2, R3 可以把 R2×R3 的结果放到 R0"
|
||||
- "答案:MOV R2, #6 / MOV R3, #7 / MUL R0, R2, R3 / MOV R2, #100 / MOV R3, #4 / DIV R1, R2, R3 / HLT"
|
||||
|
||||
starThresholds: [7, 9, 12]
|
||||
|
||||
starterCode: |
|
||||
; 计算 6×7 存入 R0
|
||||
; 计算 100÷4 存入 R1
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
57
frontend/src/lib/levels/05.yaml
Normal file
57
frontend/src/lib/levels/05.yaml
Normal file
@ -0,0 +1,57 @@
|
||||
id: 5
|
||||
title: 位运算魔法
|
||||
subtitle: 0和1的秘密
|
||||
description: 学习 AND、ORR、EOR、MVN 指令
|
||||
|
||||
tutorial:
|
||||
- title: 二进制世界
|
||||
text: >
|
||||
计算机内部用 **0** 和 **1** 存储一切。42 的二进制是 `00101010`。
|
||||
右边面板会显示每个寄存器的二进制值!
|
||||
- title: AND —— 都是1才是1
|
||||
text: >
|
||||
AND 逐位比较,两个都是 1 结果才是 1。可以用来"提取"某些位:
|
||||
code: |
|
||||
; 11111111 (255)
|
||||
; AND 00001111 (15)
|
||||
; = 00001111 (15)
|
||||
AND R0, R0, #15
|
||||
- title: 其他位运算
|
||||
text: >
|
||||
**ORR** = 有一个1就是1 (OR)
|
||||
|
||||
**EOR** = 不同才是1 (XOR)
|
||||
|
||||
**MVN** = 全部翻转 (NOT)
|
||||
code: |
|
||||
ORR R0, R0, #240 ; 设置高4位
|
||||
EOR R0, R0, #255 ; 翻转低8位
|
||||
MVN R0, R0 ; 翻转所有位
|
||||
|
||||
goal: R0 = **255** (二进制 11111111),用 AND 提取**低4位**,使 R0 变成 **15**
|
||||
|
||||
initialState:
|
||||
registers:
|
||||
R0: 255
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 15
|
||||
|
||||
hints:
|
||||
- "AND 用来保留某些位,把其他位清零"
|
||||
- "低4位的掩码是 15(二进制 00001111)"
|
||||
- "答案:AND R0, R0, #15 / HLT"
|
||||
|
||||
starThresholds: [2, 3, 5]
|
||||
|
||||
starterCode: |
|
||||
; R0 = 255 (二进制 11111111)
|
||||
; 用 AND 提取低4位
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
53
frontend/src/lib/levels/06.yaml
Normal file
53
frontend/src/lib/levels/06.yaml
Normal file
@ -0,0 +1,53 @@
|
||||
id: 6
|
||||
title: 移位操作
|
||||
subtitle: 位的舞蹈
|
||||
description: 学习 LSL 和 LSR 指令
|
||||
|
||||
tutorial:
|
||||
- title: LSL —— 逻辑左移
|
||||
text: >
|
||||
所有位向左移,右边补0。**左移1位 = 乘以2**,左移3位 = 乘以8:
|
||||
code: |
|
||||
; 5 = 00000101
|
||||
LSL R0, R0, #1 ; 00001010 = 10 (×2)
|
||||
LSL R0, R0, #1 ; 00010100 = 20 (×2)
|
||||
- title: LSR —— 逻辑右移
|
||||
text: >
|
||||
所有位向右移,左边补0。**右移1位 = 除以2**:
|
||||
code: |
|
||||
MOV R0, #40
|
||||
LSR R0, R0, #1 ; 20 (÷2)
|
||||
LSR R0, R0, #2 ; 5 (÷4)
|
||||
- title: 程序员的技巧
|
||||
text: >
|
||||
在真实的 ARM 处理器中,移位比乘除快得多!
|
||||
`LSL R0, R0, #3` 比 `MUL R0, R0, #8` 高效。
|
||||
|
||||
goal: R0 = **5**,只用**移位操作**把它变成 **40**(40 = 5 × 8 = 5 × 2³)
|
||||
|
||||
initialState:
|
||||
registers:
|
||||
R0: 5
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 40
|
||||
|
||||
hints:
|
||||
- "8 = 2³,乘以8就是左移3位"
|
||||
- "LSL R0, R0, #3"
|
||||
- "就这一条指令!"
|
||||
|
||||
starThresholds: [2, 3, 5]
|
||||
blockedOps: [MUL, DIV]
|
||||
|
||||
starterCode: |
|
||||
; R0 = 5
|
||||
; 用 LSL 让 R0 变成 40(不能用 MUL)
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
61
frontend/src/lib/levels/07.yaml
Normal file
61
frontend/src/lib/levels/07.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
id: 7
|
||||
title: 内存读写
|
||||
subtitle: 打开更大的空间
|
||||
description: 学习 LDR 和 STR 指令
|
||||
|
||||
tutorial:
|
||||
- title: 什么是内存?
|
||||
text: >
|
||||
寄存器只有8个,太少了!**内存**像一排256格的柜子,每格有编号(0-255)。
|
||||
- title: LDR —— 从内存读取
|
||||
text: >
|
||||
先把地址放进寄存器,再用 `LDR` 从那个地址读数据:
|
||||
code: |
|
||||
MOV R1, #0 ; 地址 = 0
|
||||
LDR R0, [R1] ; R0 = 内存[0]
|
||||
- title: STR —— 写入内存
|
||||
text: >
|
||||
`STR` 把寄存器的值写到内存:
|
||||
code: |
|
||||
MOV R1, #5 ; 地址 = 5
|
||||
STR R0, [R1] ; 内存[5] = R0
|
||||
- title: 偏移寻址
|
||||
text: >
|
||||
还可以加偏移量:`[R1, #4]` 表示地址 R1+4:
|
||||
code: |
|
||||
MOV R1, #0
|
||||
LDR R0, [R1, #0] ; 内存[0]
|
||||
LDR R2, [R1, #1] ; 内存[1]
|
||||
|
||||
goal: 内存[0]=**10**,内存[1]=**20**,计算它们的和存入 **内存[2]**
|
||||
|
||||
initialState:
|
||||
memory:
|
||||
0: 10
|
||||
1: 20
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
memory:
|
||||
2: 30
|
||||
|
||||
hints:
|
||||
- "先用 LDR 把内存值读到寄存器,算完用 STR 写回"
|
||||
- "MOV R3, #0 设基地址,LDR R0, [R3, #0] 读第一个值"
|
||||
- "答案:MOV R3, #0 / LDR R0, [R3, #0] / LDR R1, [R3, #1] / ADD R2, R0, R1 / STR R2, [R3, #2] / HLT"
|
||||
|
||||
starThresholds: [6, 8, 10]
|
||||
|
||||
starterCode: |
|
||||
; 内存[0]=10, 内存[1]=20
|
||||
; 计算它们的和,存入内存[2]
|
||||
;
|
||||
; 提示:先 MOV 一个地址到寄存器
|
||||
; 然后用 LDR/STR 读写内存
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: true
|
||||
memoryRange: [0, 15]
|
||||
77
frontend/src/lib/levels/08.yaml
Normal file
77
frontend/src/lib/levels/08.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
id: 8
|
||||
title: 比较与跳转
|
||||
subtitle: 让程序会做决定
|
||||
description: 学习 CMP 和条件分支指令
|
||||
|
||||
tutorial:
|
||||
- title: 到目前为止...
|
||||
text: >
|
||||
程序都是从头到尾顺序执行。但有了**分支**,程序就能做决定了!
|
||||
- title: CMP —— 比较
|
||||
text: >
|
||||
`CMP` 比较两个值,记住比较结果(不会改变它们的值):
|
||||
code: |
|
||||
CMP R0, #10 ; 比较 R0 和 10
|
||||
- title: 条件分支
|
||||
text: >
|
||||
比较后用 **B** (Branch=分支) 跳转:
|
||||
code: |
|
||||
BEQ label ; 等于则跳(Equal)
|
||||
BNE label ; 不等则跳(Not Equal)
|
||||
BGT label ; 大于则跳(Greater Than)
|
||||
BLT label ; 小于则跳(Less Than)
|
||||
B label ; 无条件跳
|
||||
- title: 标签
|
||||
text: >
|
||||
**标签**是代码里的记号,分支指令跳到标签位置。标签后面加冒号:
|
||||
code: |
|
||||
CMP R0, #10
|
||||
BGT big
|
||||
MOV R1, #0 ; R0 <= 10
|
||||
B done ; 跳过下面
|
||||
big:
|
||||
MOV R1, #1 ; R0 > 10
|
||||
done:
|
||||
HLT
|
||||
|
||||
goal: R0=**15**。如果 R0 > 10 则 R1 = **1**;否则 R1 = **0**
|
||||
|
||||
initialState:
|
||||
registers:
|
||||
R0: 15
|
||||
|
||||
testCases:
|
||||
- init:
|
||||
registers:
|
||||
R0: 15
|
||||
expected:
|
||||
registers:
|
||||
R1: 1
|
||||
- init:
|
||||
registers:
|
||||
R0: 5
|
||||
expected:
|
||||
registers:
|
||||
R1: 0
|
||||
- init:
|
||||
registers:
|
||||
R0: 10
|
||||
expected:
|
||||
registers:
|
||||
R1: 0
|
||||
|
||||
hints:
|
||||
- "先设 R1=#0(默认),再比较 R0 和 10"
|
||||
- "如果 R0 > 10,跳到标签把 R1 改成 1"
|
||||
- "答案:MOV R1, #0 / CMP R0, #10 / BLE done / MOV R1, #1 / done: HLT"
|
||||
|
||||
starThresholds: [5, 7, 9]
|
||||
|
||||
starterCode: |
|
||||
; 如果 R0 > 10,则 R1 = 1
|
||||
; 否则 R1 = 0
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
50
frontend/src/lib/levels/09.yaml
Normal file
50
frontend/src/lib/levels/09.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
id: 9
|
||||
title: 循环
|
||||
subtitle: 重复的力量
|
||||
description: 用分支指令创建循环
|
||||
|
||||
tutorial:
|
||||
- title: 什么是循环?
|
||||
text: >
|
||||
循环让一段代码**反复执行**。在汇编中,循环就是**跳回前面的标签**!
|
||||
- title: 循环结构
|
||||
text: >
|
||||
①初始化 ②做事 ③更新计数器 ④判断+跳回:
|
||||
code: |
|
||||
MOV R4, #0 ; ① 初始化
|
||||
loop: ; 循环开始
|
||||
ADD R4, R4, #1 ; ②③ 计数+1
|
||||
CMP R4, #5 ; ④ 到5了吗?
|
||||
BLE loop ; 没到就跳回
|
||||
HLT
|
||||
- title: 注意!
|
||||
text: >
|
||||
忘了更新计数器 = **死循环**(别担心,超过10000步会自动停止)。
|
||||
|
||||
goal: 计算 **1+2+3+...+10** 的和存入 **R0**(答案是55)
|
||||
|
||||
initialState: {}
|
||||
|
||||
testCases:
|
||||
- init: {}
|
||||
expected:
|
||||
registers:
|
||||
R0: 55
|
||||
|
||||
hints:
|
||||
- "R0 累加结果,R4 做计数器(1到10)"
|
||||
- "循环体:ADD R0, R0, R4 / ADD R4, R4, #1 / CMP R4, #10 / BLE loop"
|
||||
- "完整:MOV R0, #0 / MOV R4, #1 / loop: ADD R0, R0, R4 / ADD R4, R4, #1 / CMP R4, #10 / BLE loop / HLT"
|
||||
|
||||
starThresholds: [7, 9, 12]
|
||||
|
||||
starterCode: |
|
||||
; 计算 1+2+3+...+10
|
||||
; 结果存入 R0
|
||||
;
|
||||
; 提示:用一个寄存器做计数器
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: false
|
||||
81
frontend/src/lib/levels/10.yaml
Normal file
81
frontend/src/lib/levels/10.yaml
Normal file
@ -0,0 +1,81 @@
|
||||
id: 10
|
||||
title: 终极挑战
|
||||
subtitle: 寻找最大值
|
||||
description: 综合运用所有技能!
|
||||
|
||||
tutorial:
|
||||
- title: 最后一关!
|
||||
text: >
|
||||
你已经学会了寄存器、算术、位运算、内存、分支和循环。
|
||||
现在把**所有技能**结合起来!
|
||||
- title: 挑战说明
|
||||
text: >
|
||||
内存地址 0-4 存了5个数字。你要找到**最大值**和它的**位置**。
|
||||
需要:循环 + 内存读取 + 比较分支。
|
||||
- title: 解题思路
|
||||
text: |
|
||||
1. 假设第一个数最大(R0=内存[0],R1=位置0)
|
||||
2. 循环检查剩余的数
|
||||
3. 如果发现更大的,更新最大值和位置
|
||||
4. 直到检查完全部5个数
|
||||
code: |
|
||||
; 伪代码:
|
||||
; R0 = max = mem[0]
|
||||
; R1 = maxIdx = 0
|
||||
; for R4 = 1 to 4:
|
||||
; R5 = mem[R4]
|
||||
; if R5 > R0: R0=R5, R1=R4
|
||||
|
||||
goal: 内存[0..4] 有5个数,找出**最大值**存入 **R0**,其**位置**存入 **R1**
|
||||
|
||||
initialState:
|
||||
memory:
|
||||
0: 5
|
||||
1: 3
|
||||
2: 8
|
||||
3: 1
|
||||
4: 7
|
||||
|
||||
testCases:
|
||||
- init:
|
||||
memory:
|
||||
0: 5
|
||||
1: 3
|
||||
2: 8
|
||||
3: 1
|
||||
4: 7
|
||||
expected:
|
||||
registers:
|
||||
R0: 8
|
||||
R1: 2
|
||||
- init:
|
||||
memory:
|
||||
0: 1
|
||||
1: 9
|
||||
2: 4
|
||||
3: 9
|
||||
4: 2
|
||||
expected:
|
||||
registers:
|
||||
R0: 9
|
||||
R1: 1
|
||||
|
||||
hints:
|
||||
- "R0=最大值, R1=位置, R4=循环计数器, R5=当前值"
|
||||
- "用 MOV R3, R4 / LDR R5, [R3] 来读取 mem[R4]"
|
||||
- "循环体:MOV R3, R4 / LDR R5, [R3] / CMP R5, R0 / BLE skip / MOV R0, R5 / MOV R1, R4 / skip: ADD R4, R4, #1 / CMP R4, #5 / BLT loop"
|
||||
|
||||
starThresholds: [12, 15, 20]
|
||||
|
||||
starterCode: |
|
||||
; 内存[0..4] = [5, 3, 8, 1, 7]
|
||||
; 找最大值存入 R0,位置存入 R1
|
||||
;
|
||||
; 提示:用 R4 做循环变量
|
||||
; 用 MOV + LDR 读取内存
|
||||
|
||||
|
||||
HLT
|
||||
|
||||
showMemory: true
|
||||
memoryRange: [0, 15]
|
||||
@ -20,7 +20,6 @@
|
||||
@click="go(level)"
|
||||
>
|
||||
<div class="level-num">{{ level.id }}</div>
|
||||
<div class="level-icon">{{ level.icon }}</div>
|
||||
<h3>{{ level.title }}</h3>
|
||||
<p class="card-subtitle">{{ level.subtitle }}</p>
|
||||
<p class="card-desc">{{ level.description }}</p>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<header class="level-header">
|
||||
<router-link to="/levels" class="back-link">← 返回</router-link>
|
||||
<div class="hdr-center">
|
||||
<span class="level-badge">{{ level.icon }} Level {{ level.id }}</span>
|
||||
<span class="level-badge">Level {{ level.id }}</span>
|
||||
<h1>{{ level.title }}</h1>
|
||||
</div>
|
||||
<div class="hdr-stars">
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import yaml from '@modyfi/vite-plugin-yaml'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
plugins: [vue(), yaml()],
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': 'http://localhost:8000'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user