[feature] 只是能跑
This commit is contained in:
commit
ad2916b018
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "led_simulator",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"prepare": "lezer-generator src/my-asm-grammar -o src/parser.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.18.2",
|
||||||
|
"@codemirror/lang-css": "^6.3.0",
|
||||||
|
"@codemirror/lang-html": "^6.4.9",
|
||||||
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
|
"@codemirror/lint": "^6.8.2",
|
||||||
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
|
"@codemirror/view": "^6.34.2",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
|
"codemirror-one-dark-theme": "^1.1.1",
|
||||||
|
"lezer": "^0.13.5",
|
||||||
|
"vue": "^3.5.12",
|
||||||
|
"vue-codemirror": "^6.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lezer/generator": "^1.0.0",
|
||||||
|
"@types/node": "^22.9.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"typescript": "~5.6.2",
|
||||||
|
"vite": "^5.4.10",
|
||||||
|
"vue-tsc": "^2.1.8"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,32 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {provide} from "vue";
|
||||||
|
import LEDScreen from "./components/LEDScreen.vue";
|
||||||
|
import {MemoryManager} from "./utils/MemoryManager.ts";
|
||||||
|
import {CodeChecker} from "./utils/CodeChecker.ts";
|
||||||
|
import "./style.css";
|
||||||
|
import Editors from "./components/Editors.vue";
|
||||||
|
import {Runner} from "./utils/Runner.ts";
|
||||||
|
import ControlPanel from "./components/ControlPanel.vue";
|
||||||
|
import {Compiler} from "./utils/Complier.ts";
|
||||||
|
|
||||||
|
let memManager = new MemoryManager();
|
||||||
|
let compiler = new Compiler();
|
||||||
|
provide("compiler", compiler);
|
||||||
|
provide("memoryManager", memManager);
|
||||||
|
provide("codeChecker", new CodeChecker());
|
||||||
|
provide("runner", new Runner(memManager,compiler));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>LED显示屏模拟器</h1>
|
||||||
|
<p>made by li_chx</p>
|
||||||
|
<LEDScreen/>
|
||||||
|
<ControlPanel/>
|
||||||
|
<Editors/>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
|
@ -0,0 +1,121 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {inject, ref} from "vue";
|
||||||
|
import "../style.css"
|
||||||
|
import {CodeChecker} from "../utils/CodeChecker.ts";
|
||||||
|
import {Codemirror} from "vue-codemirror";
|
||||||
|
// @ts-ignore
|
||||||
|
import {parser} from "../utils/parser.js"
|
||||||
|
import {foldNodeProp, foldInside} from "@codemirror/language"
|
||||||
|
import {styleTags, tags as t} from "@lezer/highlight"
|
||||||
|
import {LRLanguage} from "@codemirror/language"
|
||||||
|
import {completeFromList} from "@codemirror/autocomplete"
|
||||||
|
import {LanguageSupport} from "@codemirror/language"
|
||||||
|
import {oneDark} from "@codemirror/theme-one-dark"
|
||||||
|
import {keymap} from "@codemirror/view"
|
||||||
|
import {Compiler} from "../utils/Complier.ts";
|
||||||
|
import {insertTab} from "@codemirror/commands"
|
||||||
|
|
||||||
|
let compiler = inject("compiler") as Compiler;
|
||||||
|
|
||||||
|
|
||||||
|
let parserWithMetadata = parser.configure({
|
||||||
|
props: [
|
||||||
|
styleTags({
|
||||||
|
Mnemonic: t.keyword,
|
||||||
|
Register: t.variableName,
|
||||||
|
ImmediateToken: t.number,
|
||||||
|
CommentToken: t.lineComment,
|
||||||
|
LabelToken: t.labelName,
|
||||||
|
Db: t.definitionKeyword,
|
||||||
|
Dw: t.definitionKeyword,
|
||||||
|
NumberList: t.literal,
|
||||||
|
StringList: t.literal
|
||||||
|
}),
|
||||||
|
foldNodeProp.add({
|
||||||
|
Statement: foldInside
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const myASMLanguage = LRLanguage.define({
|
||||||
|
parser: parserWithMetadata,
|
||||||
|
languageData: {
|
||||||
|
commentTokens: {line: ";"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const myASMCompletion = myASMLanguage.data.of({
|
||||||
|
autocomplete: completeFromList([
|
||||||
|
...CodeChecker.KeyWords.map(keyword => ({label: keyword, type: "keyword"})),
|
||||||
|
...CodeChecker.Registers.map(register => ({label: register, type: "variableName"}))
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
function languageSupport() {
|
||||||
|
return new LanguageSupport(myASMLanguage, [myASMCompletion])
|
||||||
|
}
|
||||||
|
|
||||||
|
let code = ref("t: dw 'Hello World'\n" +
|
||||||
|
"mov \tAX, t\n" +
|
||||||
|
"ldc \tCX, 0\n" +
|
||||||
|
"ldc \tDX, 8\n" +
|
||||||
|
"ldc \tEX, 0\n" +
|
||||||
|
"ldc\t\tFX, 0\n" +
|
||||||
|
"ldc\t\tGX, 8\n" +
|
||||||
|
"lp1:\n" +
|
||||||
|
"ldc \tBX, 1\n" +
|
||||||
|
"call \t0, lp\n" +
|
||||||
|
"add \tAX, DX\n" +
|
||||||
|
"add \tCX, BX\n" +
|
||||||
|
"add\t\tFX, BX\n" +
|
||||||
|
"mov \tBX, FX\n" +
|
||||||
|
"ltjp\tGX, lp1\n" +
|
||||||
|
"\n" +
|
||||||
|
"ldc \tCX, 64\n" +
|
||||||
|
"ldc\t\tFX, 0\n" +
|
||||||
|
"ldc\t\tGX, 3\n" +
|
||||||
|
"lp2:\n" +
|
||||||
|
"ldc \tBX, 1\n" +
|
||||||
|
"call \t0, lp\n" +
|
||||||
|
"add \tAX, DX\n" +
|
||||||
|
"add \tCX, BX\n" +
|
||||||
|
"add\t\tFX, BX\n" +
|
||||||
|
"mov \tBX, FX\n" +
|
||||||
|
"ltjp\tGX, lp2\n" +
|
||||||
|
"\n" +
|
||||||
|
"\n" +
|
||||||
|
"ret \t0,0\n" +
|
||||||
|
"lp:\n" +
|
||||||
|
"ldc\t\tBX, 1\n" +
|
||||||
|
"cp\t\tAX, CX\n" +
|
||||||
|
"add\t\tAX, BX\n" +
|
||||||
|
"add\t\tCX, DX\n" +
|
||||||
|
"add\t\tEX, BX\n" +
|
||||||
|
"mov\t\tBX, EX\n" +
|
||||||
|
"ltjp\tDX,\tlp\n" +
|
||||||
|
"ret\t\t0,0");
|
||||||
|
// compiler.Code = code.value;
|
||||||
|
compiler.Code = code.value;
|
||||||
|
|
||||||
|
function onCodeChange() {
|
||||||
|
compiler.Code = code.value;
|
||||||
|
compiler.CodeChanged = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>Code Input</h2>
|
||||||
|
<codemirror class="CodeMirror custom-codemirror" v-model="code" style="height: 500px;"
|
||||||
|
:extensions="[languageSupport(), oneDark, keymap.of([{key: 'Tab', run: (view) => insertTab(view)}])]"
|
||||||
|
:options="{lineNumbers: true}"
|
||||||
|
@update:modelValue="onCodeChange"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-codemirror {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {Runner, RunnerState} from "../utils/Runner.ts";
|
||||||
|
import {inject} from "vue";
|
||||||
|
import {Compiler} from "../utils/Complier.ts";
|
||||||
|
let runner:Runner = inject("runner") as Runner;
|
||||||
|
console.log(runner);
|
||||||
|
let compiler = inject("compiler") as Compiler;
|
||||||
|
function compileCode() {
|
||||||
|
runner.Stop();
|
||||||
|
compiler.CompileCode();
|
||||||
|
}
|
||||||
|
function startOrContinue() {
|
||||||
|
switch (runner.State)
|
||||||
|
{
|
||||||
|
case RunnerState.Running:
|
||||||
|
break;
|
||||||
|
case RunnerState.Step:
|
||||||
|
runner.Resume();
|
||||||
|
break;
|
||||||
|
case RunnerState.Stop:
|
||||||
|
runner.Run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function step() {
|
||||||
|
if(compiler.CodeChanged)
|
||||||
|
{
|
||||||
|
compiler.CompileCode();
|
||||||
|
}
|
||||||
|
runner.Step();
|
||||||
|
}
|
||||||
|
function stop() {
|
||||||
|
runner.Stop();
|
||||||
|
}
|
||||||
|
function refresh() {
|
||||||
|
runner.Refresh();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="controlPanel">
|
||||||
|
<div></div>
|
||||||
|
<div id="buttons">
|
||||||
|
<img class="button" src="../assets/compile.png" alt="编译" @click="compileCode">
|
||||||
|
<img class="button" src="../assets/play.png" alt="运行/恢复运行" @click="startOrContinue">
|
||||||
|
<img class="button" src="../assets/step.png" alt="步过" @click="step">
|
||||||
|
<img class="button" src="../assets/stop.png" alt="停止" @click="stop">
|
||||||
|
<img class="button" src="../assets/refresh.png" alt="重新调试" @click="refresh">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#controlPanel {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#controlPanel > :first-child {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
#controlPanel > :last-child {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#buttons{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button{
|
||||||
|
flex: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 3px;
|
||||||
|
padding: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.button:hover{
|
||||||
|
background-color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,147 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import CodeInput from "./CodeInput.vue";
|
||||||
|
import MemoryLayout from "./MemoryLayout.vue";
|
||||||
|
import MachineCodeLayout from "./MachineCodeLayout.vue";
|
||||||
|
import "../style.css";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
|
||||||
|
const startX = ref(0);
|
||||||
|
const parentWidth = ref(0);
|
||||||
|
const codeInputFlex = ref(1);
|
||||||
|
const machineCodeLayoutFlex = ref(1);
|
||||||
|
const memoryLayoutFlex = ref(1);
|
||||||
|
const startCodeInputFlex = ref(1);
|
||||||
|
const startMachineCodeLayoutFlex = ref(1);
|
||||||
|
const startMemoryLayoutFlex = ref(1);
|
||||||
|
const movingSplitId = ref("");
|
||||||
|
const cssVars = computed(() => ({
|
||||||
|
'--code-input-flex': codeInputFlex.value,
|
||||||
|
'--machine-code-layout-flex': machineCodeLayoutFlex.value,
|
||||||
|
'--memory-layout-flex': memoryLayoutFlex.value
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
function onMouseDown(event: MouseEvent) {
|
||||||
|
const htmlElement = event.target as HTMLElement;
|
||||||
|
parentWidth.value = htmlElement.parentElement!.offsetWidth - 12;
|
||||||
|
|
||||||
|
startX.value = event.clientX;
|
||||||
|
startCodeInputFlex.value = codeInputFlex.value;
|
||||||
|
startMachineCodeLayoutFlex.value = machineCodeLayoutFlex.value;
|
||||||
|
startMemoryLayoutFlex.value = memoryLayoutFlex.value;
|
||||||
|
if (htmlElement.id === "split1") {
|
||||||
|
movingSplitId.value = "split1"
|
||||||
|
} else if (htmlElement.id === "split2") {
|
||||||
|
movingSplitId.value = "split2"
|
||||||
|
}
|
||||||
|
document.body.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.body.addEventListener('mouseup', onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseMove(event: MouseEvent) {
|
||||||
|
const minLimit = 500 / parentWidth.value;
|
||||||
|
|
||||||
|
function limit(t: number, maxLimit: number): number {
|
||||||
|
t = Math.max(t, minLimit);
|
||||||
|
t = Math.min(t, maxLimit);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movingSplitId.value === "split1") {
|
||||||
|
const maxLimit = startCodeInputFlex.value + startMachineCodeLayoutFlex.value - minLimit;
|
||||||
|
codeInputFlex.value = limit((startCodeInputFlex.value * parentWidth.value + 3 * (event.clientX - startX.value)) / parentWidth.value, maxLimit);
|
||||||
|
machineCodeLayoutFlex.value = limit((startMachineCodeLayoutFlex.value * parentWidth.value - 3 * (event.clientX - startX.value)) / parentWidth.value, maxLimit);
|
||||||
|
} else if (movingSplitId.value === "split2") {
|
||||||
|
const maxLimit = startMachineCodeLayoutFlex.value + startMemoryLayoutFlex.value - minLimit;
|
||||||
|
machineCodeLayoutFlex.value = limit((startMachineCodeLayoutFlex.value * parentWidth.value + 3 * (event.clientX - startX.value)) / parentWidth.value, maxLimit);
|
||||||
|
memoryLayoutFlex.value = limit((startMemoryLayoutFlex.value * parentWidth.value - 3 * (event.clientX - startX.value)) / parentWidth.value, maxLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseUp(event: MouseEvent) {
|
||||||
|
document.body.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.body.removeEventListener('mouseup', onMouseUp);
|
||||||
|
onMouseMove(event)
|
||||||
|
startMemoryLayoutFlex.value = memoryLayoutFlex.value;
|
||||||
|
startMachineCodeLayoutFlex.value = machineCodeLayoutFlex.value;
|
||||||
|
startCodeInputFlex.value = codeInputFlex.value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="editors">
|
||||||
|
<div id="code">
|
||||||
|
<CodeInput :style="cssVars"/>
|
||||||
|
<div id="split1" class="split" @mousedown="onMouseDown"></div>
|
||||||
|
<MachineCodeLayout :style="cssVars"/>
|
||||||
|
<div id="split2" class="split" @mousedown="onMouseDown"></div>
|
||||||
|
<MemoryLayout :style="cssVars"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
#editors {
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: rgb(55, 55, 55);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
#code {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#code > * {
|
||||||
|
background-color: rgb(30, 30, 30);
|
||||||
|
text-align: left;
|
||||||
|
flex: 3;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code > :nth-child(1) {
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
flex: var(--code-input-flex);
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code > :nth-child(3) {
|
||||||
|
flex: var(--machine-code-layout-flex);
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code > :nth-child(5) {
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
flex: var(--memory-layout-flex);
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code .split {
|
||||||
|
flex: 0;
|
||||||
|
cursor: ew-resize;
|
||||||
|
background-color: rgb(55, 55, 55);
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code .split:hover {
|
||||||
|
background-color: rgb(70, 70, 70);
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed, inject} from "vue";
|
||||||
|
import SingleLED from "./SingleLED.vue";
|
||||||
|
import {MemoryManager} from "../utils/MemoryManager.ts";
|
||||||
|
|
||||||
|
let memoryManager = inject("memoryManager") as MemoryManager;
|
||||||
|
let screenMemory = computed(() => {
|
||||||
|
return memoryManager.screenMemory.value.map(row => row.map(byte => byte.value));
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="screen">
|
||||||
|
<div class="screen-row" v-for="(row, rowIndex) in screenMemory" :key="rowIndex">
|
||||||
|
<div v-for="(col, colIndex) in row" :key="colIndex" class="led-group">
|
||||||
|
<SingleLED v-for="(led, ledIndex) in col.slice().reverse()" :rowIndex="rowIndex" :colIndex="colIndex"
|
||||||
|
:ledIndex="ledIndex" v-bind:isActive="led"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.screen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
white-space: nowrap; /* Prevent wrapping */
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import "../style.css"
|
||||||
|
import {inject, Ref, ref} from "vue";
|
||||||
|
import {Compiler} from "../utils/Complier.ts";
|
||||||
|
import {Runner, RunnerState} from "../utils/Runner.ts";
|
||||||
|
|
||||||
|
let compiler: Compiler = inject("compiler") as Compiler;
|
||||||
|
let runner: Runner = inject("runner") as Runner;
|
||||||
|
let machineCode: Ref<string[][], string[][]> = ref([]);
|
||||||
|
let currentPC: Ref<number, number> = ref(-1);
|
||||||
|
compiler.AddEventListener("CompileFinished", (_) => {
|
||||||
|
machineCode.value = Array.from(compiler.PCToMachineCode.values()).map((x) => {
|
||||||
|
x = x.slice(2);
|
||||||
|
return [x.slice(0, 2), x.slice(2, 4), x.slice(4, 6)];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
runner.AddEventListener("PCChanged", (_) => {
|
||||||
|
currentPC.value = runner.State===RunnerState.Stop?-1: runner._processCounter;
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>Machine Code Layout</h2>
|
||||||
|
<div id="code-area">
|
||||||
|
<div v-for="(instruction,index) in machineCode">
|
||||||
|
<div class="code" :class="{currentCode: index === currentPC}">
|
||||||
|
<span class="line-number">{{ index + 1 }}</span>
|
||||||
|
<div class="gutter"></div>
|
||||||
|
<span class="mnemonic">{{ instruction[0] }}</span>
|
||||||
|
<span class="operand">{{ instruction[1] }}</span>
|
||||||
|
<span class="operand">{{ instruction[2] }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#code-area {
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
background-color: #282c34;
|
||||||
|
height: 492px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currentCode {
|
||||||
|
background-color: #3e4451;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
/*这里的显示需要和 Assembly Code 对齐 但我不知道CodeMirror的行高是怎么算出来的
|
||||||
|
反正edge样式中是这个数,但是其他设备很可能会不对齐*/
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline;
|
||||||
|
font-family: monospace;
|
||||||
|
line-height: 28px;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-number {
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 5px;
|
||||||
|
width: 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: rgb(125, 135, 153);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mnemonic {
|
||||||
|
color: rgb(198, 120, 221);
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operand {
|
||||||
|
color: rgb(224, 108, 117);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
display: inline;
|
||||||
|
width: 9px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,163 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import "../style.css"
|
||||||
|
import {MemoryManager} from "../utils/MemoryManager.ts";
|
||||||
|
import {computed, inject, onMounted, Ref, ref} from "vue";
|
||||||
|
import {Runner} from "../utils/Runner.ts";
|
||||||
|
|
||||||
|
const looseLayout = ref(false);
|
||||||
|
onMounted(() => {
|
||||||
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
|
looseLayout.value = entries[0].target.clientWidth > 525;
|
||||||
|
});
|
||||||
|
resizeObserver.observe(document.querySelector('#memories')!);
|
||||||
|
});
|
||||||
|
|
||||||
|
let memoryManager = inject("memoryManager") as MemoryManager;
|
||||||
|
let runner = inject("runner") as Runner;
|
||||||
|
let registers: Ref<string[], string[]> = ref([...runner.GetRegisters().map((x) => (x.GetValue(16) as string)) , "00"]);
|
||||||
|
runner.AddEventListener("RegistersChanged", (_) => {
|
||||||
|
registers.value = [...runner.GetRegisters().map((x) => (x.GetValue(16) as string)), runner.GetPC().toString(16).padStart(2, '0')];
|
||||||
|
})
|
||||||
|
let screenMemory = computed(() => {
|
||||||
|
return memoryManager.screenMemory.value.map(row => row.map(byte => byte.GetValue(16) as string));
|
||||||
|
})
|
||||||
|
let memory = computed(() => {
|
||||||
|
let mem = memoryManager.memory;
|
||||||
|
let res:string[][];
|
||||||
|
if(looseLayout.value)
|
||||||
|
{
|
||||||
|
res = Array.from({length: 8}, () => Array.from({length: 16}, () => ""));
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
for (let j = 0; j < 16; j++) {
|
||||||
|
res[i][j] = mem.value[i * 16 + j].GetValue(16) as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = Array.from({length: 16}, () => Array.from({length: 8}, () => ""));
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
for (let j = 0; j < 8; j++) {
|
||||||
|
res[i][j] = mem.value[i * 8 + j].GetValue(16) as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>Memory Layout</h2>
|
||||||
|
<div id="memories">
|
||||||
|
<p>Screen Memory:</p>
|
||||||
|
<div id="screen-memory-area">
|
||||||
|
<div class="screen-row" v-for="(row, rowIndex) in screenMemory" :key="rowIndex">
|
||||||
|
<span class="screen-line-number">{{ rowIndex }}</span>
|
||||||
|
<div class="gutter"></div>
|
||||||
|
<span v-for="(col, colIndex) in row" :key="colIndex" class="screen-memory">
|
||||||
|
{{ col }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Registers:</p>
|
||||||
|
<div id="registers-area"
|
||||||
|
:class="{'register-loose-layout':looseLayout, 'register-compact-layout':!looseLayout}">
|
||||||
|
<div class="registers" v-for="(register,index) in registers">
|
||||||
|
<span class="register-name">{{ index!=7? String.fromCharCode('A'.charCodeAt(0) + index) + 'X':'PC' }}</span>
|
||||||
|
<span class="register-value">{{ register }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Memory:</p>
|
||||||
|
<div id="screen-memory-area">
|
||||||
|
<div class="screen-row" v-for="(row, rowIndex) in memory" :key="rowIndex">
|
||||||
|
<span class="screen-line-number">{{ rowIndex }}</span>
|
||||||
|
<div class="gutter"></div>
|
||||||
|
<span v-for="(col, colIndex) in row" :key="colIndex" class="screen-memory">
|
||||||
|
{{ col }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#memories {
|
||||||
|
background-color: #282c34;
|
||||||
|
height: 492px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
padding-top: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#registers-area {
|
||||||
|
line-height: 28px;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-loose-layout {
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-compact-layout {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
color: white;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 30px;
|
||||||
|
height: 36px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline;
|
||||||
|
font-family: monospace;
|
||||||
|
line-height: 28px;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registers {
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-name {
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 5px;
|
||||||
|
color: rgb(198, 120, 221);
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-value {
|
||||||
|
color: rgb(224, 108, 117);
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-line-number {
|
||||||
|
color: rgb(125, 135, 153);
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 28px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-row {
|
||||||
|
color: rgb(224, 188, 105);
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-memory {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
display: inline-block;
|
||||||
|
width: 9px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {ref} from "vue";
|
||||||
|
const props = defineProps<{
|
||||||
|
isActive: boolean
|
||||||
|
colIndex: number
|
||||||
|
rowIndex: number
|
||||||
|
ledIndex: number
|
||||||
|
}>();
|
||||||
|
const showTooltip = ref(false);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="led-container" @mouseenter="showTooltip = true" @mouseleave="showTooltip = false">
|
||||||
|
<div class="led" :class="{ active: isActive }"></div>
|
||||||
|
<div v-if="showTooltip" class="tooltip" @mouseover="showTooltip = false">{{ props.rowIndex }},{{ props.colIndex * 8 + props.ledIndex }}
|
||||||
|
0x{{ (props.rowIndex * 8 + props.colIndex).toString(16).toUpperCase().padStart(2, '0') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.led-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.led {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: rgb(32, 42, 32);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 5px rgba(32, 42, 32, 0.5);
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: rgb(137, 221, 64);
|
||||||
|
box-shadow: 0 0 3px rgba(137, 221, 64, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import './style.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
|
@ -0,0 +1,62 @@
|
||||||
|
@top Program { Statement* }
|
||||||
|
|
||||||
|
Statement {
|
||||||
|
Label Mnemonic Operand "," Operand Comment? Newline
|
||||||
|
| Mnemonic Operand "," Operand Comment? Newline
|
||||||
|
| Label Comment? Newline
|
||||||
|
| Comment Newline
|
||||||
|
| Label Db Comment? Newline
|
||||||
|
| Label Dw Comment? Newline
|
||||||
|
}
|
||||||
|
|
||||||
|
Mnemonic {
|
||||||
|
"nop"|"ldc"|"add"|"div"|"and"|"or"|"xor"|"not"|"lsl"|"lsr"|"ld"|"st"|"mov"|"cp"|"jp"|"ltjp"|"gtjp"|"eqjp"|"call"|"ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand {
|
||||||
|
Register | Immediate
|
||||||
|
}
|
||||||
|
|
||||||
|
Register {
|
||||||
|
"AX" | "BX" | "CX" | "DX" | "EX" | "FX" | "GX"
|
||||||
|
}
|
||||||
|
|
||||||
|
Immediate {
|
||||||
|
ImmediateToken
|
||||||
|
}
|
||||||
|
|
||||||
|
Comment {
|
||||||
|
CommentToken
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
LabelToken
|
||||||
|
}
|
||||||
|
|
||||||
|
Newline {
|
||||||
|
NewlineToken
|
||||||
|
}
|
||||||
|
|
||||||
|
Db {
|
||||||
|
"db" NumberList
|
||||||
|
}
|
||||||
|
|
||||||
|
Dw {
|
||||||
|
"dw" StringList
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberList {
|
||||||
|
ImmediateToken ("," ImmediateToken)*
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList {
|
||||||
|
StringToken ("," StringToken)*
|
||||||
|
}
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
ImmediateToken { $[0-9]+ }
|
||||||
|
CommentToken { ";" $[^\n]* }
|
||||||
|
LabelToken { $[a-zA-Z_]$[a-zA-Z0-9_]* ":" }
|
||||||
|
StringToken { "'" $[a-zA-Z0-9_]* "'" }
|
||||||
|
NewlineToken { "\n" }
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
:root{
|
||||||
|
color-scheme: light dark;
|
||||||
|
background-color: rgb(30, 30, 30);
|
||||||
|
--code-input-flex: 1;
|
||||||
|
--machine-code-layout-flex: 1;
|
||||||
|
--memory-layout-flex: 1;
|
||||||
|
--current-hover-code: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#code>*>h2{
|
||||||
|
padding-left: 20px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#code>*>textarea{
|
||||||
|
background-color: rgb(55, 55, 55);
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 100em;
|
||||||
|
max-height: 420px;
|
||||||
|
resize: none;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
export class Token {
|
||||||
|
constructor(public content: string, public type: string) {}
|
||||||
|
}
|
||||||
|
export class CodeChecker {
|
||||||
|
/* - 0x00: nop, 0, 0: 无操作
|
||||||
|
- 0x01: ldc, n, number: 寄存器n赋值为number
|
||||||
|
- 0x10: add, n, m: 寄存器n的值加通用寄存器m的值,结果存回寄存器n中
|
||||||
|
- 0x11: div, n, m: 寄存器n中的值除以通用寄存器m的值,结果存回寄存器n中,并将余数存入寄存器1
|
||||||
|
- 0x12: and, n, m: 寄存器n中的值和寄存器m中的值按位与,结果存回寄存器n中
|
||||||
|
- 0x13: or, n, m: 寄存器n中的值和寄存器m中的值按位或,结果存回寄存器n中
|
||||||
|
- 0x14: xor, n, m: 寄存器n中的值和寄存器m中的值按位异或,结果存回寄存器n中
|
||||||
|
- 0x15: not, n, 0: 寄存器n中的值按位翻转
|
||||||
|
- 0x16: lsl, n, m: 寄存器n中的值左移,位数为寄存器m中的值
|
||||||
|
- 0x17: lsr, n, m: 寄存器n中的值右移,位数为寄存器m中的值
|
||||||
|
- 0x20: ld, n, addr: 将地址addr处的值加载到寄存器n中
|
||||||
|
- 0x21: st, n, addr: 将寄存器n中的值保存到地址addr处
|
||||||
|
- 0x23: mov, n, m: 将寄存器m中的值复制到寄存器n中
|
||||||
|
- 0x30: cp, src, dst: 将地址为src处的数据复制到dst处,复制的字节数为通用寄存器2的值
|
||||||
|
- 0x40: jp, 0, k: 无条件跳转到第k条指令
|
||||||
|
- 0x41: ltjp, n, k: 如果寄存器2中的值小于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x42: gtjp, n, k: 如果寄存器2中的值大于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x43: eqjp, n, k: 如果寄存器2中的值等于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x50: call, 0, k: 将7个通用寄存器及下一条指令的序号推入栈中,然后跳转到第k条指令
|
||||||
|
- 0x51: ret, 0, 0: 如果栈为空,则程序终止;如果栈不为空,则恢复7个通用寄存器的值,跳转到之前存入的第k条指令
|
||||||
|
*/
|
||||||
|
static KeyWords = ["nop","ldc","add","div","and","or","xor","not","lsl","lsr","ld","st","mov","cp","jp","ltjp","gtjp","eqjp","call","ret"];
|
||||||
|
static Registers = ["AX","BX","CX","DX","EX","FX","GX"];
|
||||||
|
static highlightCode(code: string): Token[] {
|
||||||
|
if(code.length === 0)
|
||||||
|
return [];
|
||||||
|
let res:string[] = code.split(",");
|
||||||
|
let ans:Token[] = [];
|
||||||
|
for(let key of res)
|
||||||
|
{
|
||||||
|
const temp = key.trim();
|
||||||
|
console.log(temp);
|
||||||
|
if(this.KeyWords.includes(temp))
|
||||||
|
ans.push(new Token(key, "keyword"));
|
||||||
|
else if(this.Registers.includes(temp))
|
||||||
|
ans.push(new Token(key, "register"));
|
||||||
|
else
|
||||||
|
ans.push(new Token(key, "normal"));
|
||||||
|
ans.push(new Token(",", "normal"));
|
||||||
|
}
|
||||||
|
ans.pop();
|
||||||
|
console.log(ans);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
import {Byte} from "./MemoryManager.ts";
|
||||||
|
|
||||||
|
export class CompileError extends Error {
|
||||||
|
constructor(message: string, index: number) {
|
||||||
|
super(message);
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Compiler {
|
||||||
|
// ["CompileFinished(info)", "CompileError(info,CompileError)"]
|
||||||
|
// 感觉这里写的不好 类型限制不够
|
||||||
|
_events: Map<string, ((arg0: string, arg1?: any) => void)[]> = new Map<string, ((arg0: string, arg1?: any) => void)[]>();
|
||||||
|
|
||||||
|
AddEventListener(event: string, callback: (arg0: string, arg1?: any) => void) {
|
||||||
|
if (!this._events.has(event))
|
||||||
|
this._events.set(event, [callback]);
|
||||||
|
else
|
||||||
|
this._events.get(event)!.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notify(event: string, data?: any) {
|
||||||
|
if (this._events.has(event))
|
||||||
|
if (data === undefined)
|
||||||
|
this._events.get(event)!.forEach(callback => callback(event));
|
||||||
|
else
|
||||||
|
this._events.get(event)!.forEach(callback => callback(event, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
- 0x00: nop, 0, 0: 无操作
|
||||||
|
- 0x01: ldc, n, number: 寄存器n赋值为number
|
||||||
|
- 0x10: add, n, m: 寄存器n的值加通用寄存器m的值,结果存回寄存器n中
|
||||||
|
- 0x11: div, n, m: 寄存器n中的值除以通用寄存器m的值,结果存回寄存器n中,并将余数存入寄存器1
|
||||||
|
- 0x12: and, n, m: 寄存器n中的值和寄存器m中的值按位与,结果存回寄存器n中
|
||||||
|
- 0x13: or, n, m: 寄存器n中的值和寄存器m中的值按位或,结果存回寄存器n中
|
||||||
|
- 0x14: xor, n, m: 寄存器n中的值和寄存器m中的值按位异或,结果存回寄存器n中
|
||||||
|
- 0x15: not, n, 0: 寄存器n中的值按位翻转
|
||||||
|
- 0x16: lsl, n, m: 寄存器n中的值左移,位数为寄存器m中的值
|
||||||
|
- 0x17: lsr, n, m: 寄存器n中的值右移,位数为寄存器m中的值
|
||||||
|
- 0x20: ld, n, addr: 将地址addr处的值加载到寄存器n中
|
||||||
|
- 0x21: st, n, addr: 将寄存器n中的值保存到地址addr处
|
||||||
|
- 0x23: mov, n, m: 将寄存器m中的值复制到寄存器n中
|
||||||
|
- 0x30: cp, src, dst: 将地址为src处的数据复制到dst处,复制的字节数为通用寄存器2的值
|
||||||
|
- 0x40: jp, 0, k: 无条件跳转到第k条指令
|
||||||
|
- 0x41: ltjp, n, k: 如果寄存器2中的值小于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x42: gtjp, n, k: 如果寄存器2中的值大于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x43: eqjp, n, k: 如果寄存器2中的值等于寄存器n中的值,则跳转到第k条指令
|
||||||
|
- 0x50: call, 0, k: 将7个通用寄存器及下一条指令的序号推入栈中,然后跳转到第k条指令
|
||||||
|
- 0x51: ret, 0, 0: 如果栈为空,则程序终止;如果栈不为空,则恢复7个通用寄存器的值,跳转到之前存入的第k条指令
|
||||||
|
*/
|
||||||
|
static KeyWordsToMachineCode: Map<string, string> = new Map<string, string>([
|
||||||
|
["nop", "0x00"],
|
||||||
|
["ldc", "0x01"],
|
||||||
|
["add", "0x10"],
|
||||||
|
["div", "0x11"],
|
||||||
|
["and", "0x12"],
|
||||||
|
["or", "0x13"],
|
||||||
|
["xor", "0x14"],
|
||||||
|
["not", "0x15"],
|
||||||
|
["lsl", "0x16"],
|
||||||
|
["lsr", "0x17"],
|
||||||
|
["ld", "0x20"],
|
||||||
|
["st", "0x21"],
|
||||||
|
["mov", "0x23"],
|
||||||
|
["cp", "0x30"],
|
||||||
|
["jp", "0x40"],
|
||||||
|
["ltjp", "0x41"],
|
||||||
|
["gtjp", "0x42"],
|
||||||
|
["eqjp", "0x43"],
|
||||||
|
["call", "0x50"],
|
||||||
|
["ret", "0x51"]
|
||||||
|
]);
|
||||||
|
static Registers: Map<string, string> = new Map<string, string>([
|
||||||
|
["AX", "01"],
|
||||||
|
["BX", "02"],
|
||||||
|
["CX", "03"],
|
||||||
|
["DX", "04"],
|
||||||
|
["EX", "05"],
|
||||||
|
["FX", "06"],
|
||||||
|
["GX", "07"]
|
||||||
|
])
|
||||||
|
static font8x8: { [key: string]: number[] } = {
|
||||||
|
'A': [0x7E, 0x11, 0x11, 0x7E, 0x11, 0x11, 0x11, 0x00],
|
||||||
|
'B': [0x7C, 0x12, 0x12, 0x7C, 0x12, 0x12, 0x7C, 0x00],
|
||||||
|
'C': [0x3E, 0x41, 0x40, 0x40, 0x40, 0x41, 0x3E, 0x00],
|
||||||
|
'D': [0x7C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7C, 0x00],
|
||||||
|
'E': [0x7F, 0x40, 0x40, 0x7C, 0x40, 0x40, 0x7F, 0x00],
|
||||||
|
'F': [0x7F, 0x40, 0x40, 0x7C, 0x40, 0x40, 0x40, 0x00],
|
||||||
|
'G': [0x3E, 0x41, 0x40, 0x4F, 0x41, 0x41, 0x3E, 0x00],
|
||||||
|
'H': [0x41, 0x41, 0x41, 0x7F, 0x41, 0x41, 0x41, 0x00],
|
||||||
|
'I': [0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00],
|
||||||
|
'J': [0x0F, 0x02, 0x02, 0x02, 0x02, 0x42, 0x3C, 0x00],
|
||||||
|
'K': [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11, 0x00],
|
||||||
|
'L': [0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x00],
|
||||||
|
'M': [0x41, 0x63, 0x55, 0x49, 0x41, 0x41, 0x41, 0x00],
|
||||||
|
'N': [0x41, 0x61, 0x51, 0x49, 0x45, 0x43, 0x41, 0x00],
|
||||||
|
'O': [0x3E, 0x41, 0x41, 0x41, 0x41, 0x41, 0x3E, 0x00],
|
||||||
|
'P': [0x7E, 0x11, 0x11, 0x7E, 0x40, 0x40, 0x40, 0x00],
|
||||||
|
'Q': [0x3E, 0x41, 0x41, 0x41, 0x45, 0x42, 0x3D, 0x00],
|
||||||
|
'R': [0x7E, 0x41, 0x41, 0x7E, 0x44, 0x42, 0x41, 0x00],
|
||||||
|
'S': [0x3E, 0x41, 0x40, 0x3E, 0x01, 0x41, 0x3E, 0x00],
|
||||||
|
'T': [0x7F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00],
|
||||||
|
'U': [0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x3E, 0x00],
|
||||||
|
'V': [0x41, 0x41, 0x41, 0x41, 0x22, 0x14, 0x08, 0x00],
|
||||||
|
'W': [0x41, 0x41, 0x41, 0x49, 0x55, 0x63, 0x41, 0x00],
|
||||||
|
'X': [0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00],
|
||||||
|
'Y': [0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00],
|
||||||
|
'Z': [0x7F, 0x02, 0x04, 0x08, 0x10, 0x20, 0x7F, 0x00],
|
||||||
|
'0': [0x3E, 0x41, 0x43, 0x45, 0x49, 0x51, 0x3E, 0x00],
|
||||||
|
'1': [0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00],
|
||||||
|
'2': [0x3E, 0x41, 0x01, 0x3E, 0x40, 0x40, 0x7F, 0x00],
|
||||||
|
'3': [0x3E, 0x41, 0x01, 0x1E, 0x01, 0x41, 0x3E, 0x00],
|
||||||
|
'4': [0x10, 0x30, 0x50, 0x90, 0x7F, 0x10, 0x10, 0x00],
|
||||||
|
'5': [0x7F, 0x40, 0x7E, 0x01, 0x01, 0x41, 0x3E, 0x00],
|
||||||
|
'6': [0x3E, 0x40, 0x7E, 0x41, 0x41, 0x41, 0x3E, 0x00],
|
||||||
|
'7': [0x7F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00],
|
||||||
|
'8': [0x3E, 0x41, 0x41, 0x3E, 0x41, 0x41, 0x3E, 0x00],
|
||||||
|
'9': [0x3E, 0x41, 0x41, 0x3F, 0x01, 0x41, 0x3E, 0x00],
|
||||||
|
'+': [0x00, 0x08, 0x08, 0x7F, 0x08, 0x08, 0x00, 0x00],
|
||||||
|
'-': [0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00],
|
||||||
|
'x': [0x00, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41],
|
||||||
|
'/': [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00],
|
||||||
|
' ': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
};
|
||||||
|
Code: string = "";
|
||||||
|
CodeChanged: boolean = true;
|
||||||
|
PCToIndex: Map<number, number> = new Map<number, number>();
|
||||||
|
PCToCode: Map<number, string> = new Map<number, string>();
|
||||||
|
PCToMachineCode: Map<number, string> = new Map<number, string>();
|
||||||
|
Labels: Map<string, number> = new Map<string, number>();
|
||||||
|
MemorySet: Byte[] = [];
|
||||||
|
|
||||||
|
Clear() {
|
||||||
|
this.PCToIndex.clear();
|
||||||
|
this.PCToCode.clear();
|
||||||
|
this.PCToMachineCode.clear();
|
||||||
|
this.Labels.clear();
|
||||||
|
this.MemorySet = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileCode(fullCode: string = this.Code) {
|
||||||
|
this.Clear();
|
||||||
|
this.CodeChanged = false;
|
||||||
|
try {
|
||||||
|
fullCode.split("\n").forEach((line, index) => {
|
||||||
|
//code -> a: b ; c
|
||||||
|
let pc = this.PCToCode.size;
|
||||||
|
let labelAndCode = (line.includes(";") ? line.slice(0, line.indexOf(";")) : line).split(":");
|
||||||
|
let code = "";
|
||||||
|
if (labelAndCode.length === 2) {
|
||||||
|
if (this.Labels.has(labelAndCode[0].trim()))
|
||||||
|
throw new Error("Duplicate label");
|
||||||
|
this.Labels.set(labelAndCode[0].trim(), pc);
|
||||||
|
code = labelAndCode[1].trim();
|
||||||
|
} else if (labelAndCode.length === 1)
|
||||||
|
code = labelAndCode[0].trim();
|
||||||
|
else
|
||||||
|
throw new CompileError("Invalid code", index);
|
||||||
|
if (code === "")
|
||||||
|
return;
|
||||||
|
if (code.startsWith("db")) {
|
||||||
|
if (labelAndCode.length === 1)
|
||||||
|
throw new CompileError("db must start with a label.", index);
|
||||||
|
this.Labels.set(labelAndCode[0].trim(), this.MemorySet.length + 128);
|
||||||
|
code.slice(2).split(",").forEach(val => {
|
||||||
|
let num = val.toUpperCase().endsWith('H') ? parseInt(val, 16) : parseInt(val);
|
||||||
|
if (isNaN(num) || num < 0 || num > 255)
|
||||||
|
throw new CompileError("Invalid number", index);
|
||||||
|
this.MemorySet.push(new Byte(num));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (code.startsWith("dw")) {
|
||||||
|
if (labelAndCode.length === 1)
|
||||||
|
throw new CompileError("dw must start with a label.", index);
|
||||||
|
this.Labels.set(labelAndCode[0].trim(), this.MemorySet.length + 128);
|
||||||
|
code.slice(3).toUpperCase().split(",").forEach(val => {
|
||||||
|
let str = val.slice(1, -1);
|
||||||
|
for (let c of str) {
|
||||||
|
if (!(c in Compiler.font8x8))
|
||||||
|
throw new CompileError("Invalid char", index);
|
||||||
|
Compiler.font8x8[c].forEach(byte => this.MemorySet.push(new Byte(byte)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.PCToCode.set(pc, code);
|
||||||
|
this.PCToIndex.set(pc, index);
|
||||||
|
});
|
||||||
|
console.log(this.Labels);
|
||||||
|
this.PCToCode.forEach((code, pc) => {
|
||||||
|
let index = this.PCToIndex.get(pc)!;
|
||||||
|
let mnemonicAndVals = code.split(/[\s,]+/);
|
||||||
|
if (mnemonicAndVals.length !== 3)
|
||||||
|
throw new CompileError("Invalid code", index);
|
||||||
|
if (!Compiler.KeyWordsToMachineCode.has(mnemonicAndVals[0]))
|
||||||
|
throw new CompileError("Invalid mnemonic", index);
|
||||||
|
for (let i = 1; i <= 2; i++)
|
||||||
|
if ((Compiler.Registers.has(mnemonicAndVals[i])))
|
||||||
|
mnemonicAndVals[i] = Compiler.Registers.get(mnemonicAndVals[i])!;
|
||||||
|
else if(this.Labels.has(mnemonicAndVals[i]))
|
||||||
|
mnemonicAndVals[i] = this.Labels.get(mnemonicAndVals[i])!.toString(16).padStart(2,'0');
|
||||||
|
else
|
||||||
|
mnemonicAndVals[i] = parseInt(mnemonicAndVals[i]).toString(16).padStart(2,'0');
|
||||||
|
this.PCToMachineCode.set(pc, Compiler.KeyWordsToMachineCode.get(mnemonicAndVals[0])! + mnemonicAndVals[1] + mnemonicAndVals[2]);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof CompileError)
|
||||||
|
this.Notify("CompileError", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
this.Notify("CompileFinished");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
import {ref, Ref} from "vue";
|
||||||
|
|
||||||
|
export class Byte {
|
||||||
|
value: boolean[]
|
||||||
|
|
||||||
|
constructor();
|
||||||
|
constructor(value: boolean[]);
|
||||||
|
constructor(value: number);
|
||||||
|
constructor(value: Byte);
|
||||||
|
constructor(value?: boolean[] | number | Byte) {
|
||||||
|
if (value) {
|
||||||
|
if(value instanceof Byte)
|
||||||
|
{
|
||||||
|
this.value = value.value.slice();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (typeof value === 'number') {
|
||||||
|
if (value < 0 || value >= 256 || !Number.isInteger(value))
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
this.value = Array(8).fill(false);
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
this.value[i] = (value & (1 << i)) !== 0;
|
||||||
|
}
|
||||||
|
} else if (value.length === 8)
|
||||||
|
this.value = value;
|
||||||
|
else
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
} else
|
||||||
|
this.value = Array(8).fill(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetValue(num: number | string | Byte) {
|
||||||
|
if(num instanceof Byte)
|
||||||
|
{
|
||||||
|
this.value = num.value.slice();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof num === 'string') {
|
||||||
|
num = parseInt(num);
|
||||||
|
}
|
||||||
|
if (num < 0 || num >= 256)
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
this.value = Array(8).fill(false);
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
this.value[i] = (num & (1 << i)) !== 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetValue(format: number = 10): number | string {
|
||||||
|
if (format === 10)
|
||||||
|
return this.value.reduce((acc, val, index) => acc + (val ? 2 ** index : 0), 0);
|
||||||
|
if (format === 16)
|
||||||
|
return this.value.reduce((acc, val, index) => acc + (val ? 2 ** index : 0), 0).toString(16).padStart(2, '0');
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
Copy(){
|
||||||
|
return new Byte(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ignore carry
|
||||||
|
Add(b: Byte) {
|
||||||
|
return new Byte(((this.GetValue() as number) + (b.GetValue() as number)) % 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ignore carry
|
||||||
|
AddAndAssign(b: Byte) {
|
||||||
|
this.SetValue(((this.GetValue() as number) + (b.GetValue() as number)) % 256);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sub(b: Byte) {
|
||||||
|
if (this.GetValue() < b.GetValue())
|
||||||
|
return new Byte(((this.GetValue() as number) + 256 - (b.GetValue() as number)) % 256);
|
||||||
|
return new Byte(((this.GetValue() as number) - (b.GetValue() as number)) % 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubAndAssign(b: Byte) {
|
||||||
|
if (this.GetValue() < b.GetValue()) {
|
||||||
|
this.SetValue(((this.GetValue() as number) + 256 - (b.GetValue() as number)) % 256);
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
this.SetValue(((this.GetValue() as number) - (b.GetValue() as number)) % 256);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
And(b: Byte) {
|
||||||
|
return new Byte(this.value.map((val, index) => val && b.value[index]));
|
||||||
|
}
|
||||||
|
AndAndAssign(b: Byte) {
|
||||||
|
this.value = this.value.map((val, index) => val && b.value[index]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Or(b: Byte) {
|
||||||
|
return new Byte(this.value.map((val, index) => val || b.value[index]));
|
||||||
|
}
|
||||||
|
OrAndAssign(b: Byte) {
|
||||||
|
this.value = this.value.map((val, index) => val || b.value[index]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Xor(b: Byte) {
|
||||||
|
return new Byte(this.value.map((val, index) => val !== b.value[index]));
|
||||||
|
}
|
||||||
|
XorAndAssign(b: Byte) {
|
||||||
|
this.value = this.value.map((val, index) => val !== b.value[index]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Not() {
|
||||||
|
return new Byte(this.value.map(val => !val));
|
||||||
|
}
|
||||||
|
NotAndAssign() {
|
||||||
|
this.value = this.value.map(val => !val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Lsl(b: Byte) {
|
||||||
|
return new Byte(this.value.slice(b.GetValue() as number).concat(Array(b.GetValue() as number).fill(false)));
|
||||||
|
}
|
||||||
|
LslAndAssign(b: Byte) {
|
||||||
|
this.value = this.value.slice(b.GetValue() as number).concat(Array(b.GetValue() as number).fill(false));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Lsr(b: Byte) {
|
||||||
|
return new Byte(Array(b.GetValue() as number).fill(false).concat(this.value.slice(0, 8 - (b.GetValue() as number))));
|
||||||
|
}
|
||||||
|
LsrAndAssign(b: Byte) {
|
||||||
|
this.value = Array(b.GetValue() as number).fill(false).concat(this.value.slice(0, 8 - (b.GetValue() as number)));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemoryManager {
|
||||||
|
screenMemory: Ref<Byte[][]>;
|
||||||
|
memory: Ref<Byte[]>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.screenMemory = ref(Array.from({length: 16}, () => Array.from({length: 8}, () => new Byte(255))));
|
||||||
|
this.memory = ref(Array(128).fill(new Byte(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
for (let t of this.screenMemory.value)
|
||||||
|
t.forEach(val => val.SetValue(0));
|
||||||
|
this.memory.value.forEach(val => val.SetValue(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
GetVal(t:number)
|
||||||
|
{
|
||||||
|
if(t>=128)
|
||||||
|
return this.memory.value[t-128];
|
||||||
|
else
|
||||||
|
return this.screenMemory.value[Math.floor(t/8)][t%8];
|
||||||
|
}
|
||||||
|
SetVal(t:number, val:Byte)
|
||||||
|
{
|
||||||
|
if(t>=128)
|
||||||
|
this.memory.value[t-128] = new Byte(val);
|
||||||
|
else
|
||||||
|
this.screenMemory.value[Math.floor(t/8)][t%8] = new Byte(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetMemSet(memorySet: Byte[]) {
|
||||||
|
if(memorySet.length > 128)
|
||||||
|
throw new Error("No enough space");
|
||||||
|
for(let i = 0;i<memorySet.length;i++)
|
||||||
|
this.memory.value[i] = memorySet[i];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,374 @@
|
||||||
|
import {MemoryManager, Byte} from "./MemoryManager";
|
||||||
|
import {Compiler} from "./Complier.ts";
|
||||||
|
|
||||||
|
export enum RunnerState {
|
||||||
|
Stop,
|
||||||
|
Running,
|
||||||
|
Step
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Runner {
|
||||||
|
// 与Compiler 类似的事件机制 要是再来一个我就把这段逻辑单独抽出来
|
||||||
|
//[PCChanged,RegistersChanged]
|
||||||
|
_events: Map<string, ((arg0: string, arg1?: any) => void)[]> = new Map<string, ((arg0: string, arg1?: any) => void)[]>();
|
||||||
|
|
||||||
|
AddEventListener(event: string, callback: (arg0: string, arg1?: any) => void) {
|
||||||
|
if (!this._events.has(event))
|
||||||
|
this._events.set(event, [callback]);
|
||||||
|
else
|
||||||
|
this._events.get(event)!.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notify(event: string, data?: any) {
|
||||||
|
if (this._events.has(event))
|
||||||
|
if (data === undefined)
|
||||||
|
this._events.get(event)!.forEach(callback => callback(event));
|
||||||
|
else
|
||||||
|
this._events.get(event)!.forEach(callback => callback(event, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
State: RunnerState = RunnerState.Stop;
|
||||||
|
_processor: Processor;
|
||||||
|
_memory: MemoryManager;
|
||||||
|
_processCounter: number = 0;
|
||||||
|
_compiler: Compiler;
|
||||||
|
_stack: [Byte[], number][] = [];
|
||||||
|
_intervalId: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
StartInterval() {
|
||||||
|
if (this._intervalId === null)
|
||||||
|
this._intervalId = setInterval(() => {
|
||||||
|
if (this._processCounter >= this._compiler.PCToCode.size) {
|
||||||
|
this.Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.RunOneLineCode();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
StopInterval() {
|
||||||
|
if (this._intervalId !== null) {
|
||||||
|
clearInterval(this._intervalId);
|
||||||
|
this._intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(memoryManager: MemoryManager, compiler: Compiler) {
|
||||||
|
this._memory = memoryManager
|
||||||
|
this._processor = new Processor();
|
||||||
|
this._compiler = compiler;
|
||||||
|
compiler.AddEventListener("CompileFinished", (_) => {
|
||||||
|
this._memory.SetMemSet(compiler.MemorySet)
|
||||||
|
});
|
||||||
|
// Bind methods to the instance
|
||||||
|
this.GetMemory = this.GetMemory.bind(this);
|
||||||
|
this.SetMemory = this.SetMemory.bind(this);
|
||||||
|
this.SetPC = this.SetPC.bind(this);
|
||||||
|
this.SaveEnv = this.SaveEnv.bind(this);
|
||||||
|
this.RestoreEnv = this.RestoreEnv.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandParsing(): [string, number | string, number | string] {
|
||||||
|
let code = "";
|
||||||
|
if (this._compiler.PCToCode.has(this._processCounter))
|
||||||
|
code = this._compiler.PCToCode.get(this._processCounter) as string;
|
||||||
|
else
|
||||||
|
throw new Error("PC out of range");
|
||||||
|
this._processCounter++;
|
||||||
|
let mnemonicAndVals = code.split(/[\s,]+/);
|
||||||
|
// 如果指令不合法 Compiler应该会有异常抛出 则不能执行到这里
|
||||||
|
if (mnemonicAndVals.length !== 3)
|
||||||
|
throw new Error("Invalid code");
|
||||||
|
let mnemonic = mnemonicAndVals[0];
|
||||||
|
function getVal(runner:Runner, val: string) {
|
||||||
|
if (runner._processor._registers.has(val))
|
||||||
|
return val;
|
||||||
|
else if(runner._compiler.Labels.has(val))
|
||||||
|
return runner._compiler.Labels.get(val) as number;
|
||||||
|
else {
|
||||||
|
let t = parseInt(val);
|
||||||
|
if(isNaN(t))
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [mnemonic, getVal(this,mnemonicAndVals[1]), getVal(this,mnemonicAndVals[2])];
|
||||||
|
}
|
||||||
|
|
||||||
|
RunOneLineCode() {
|
||||||
|
this._processor.RunCode(...this.CommandParsing(), this.GetMemory, this.SetMemory, this.SetPC, this.SaveEnv, this.RestoreEnv);
|
||||||
|
this.Notify("PCChanged");
|
||||||
|
this.Notify("RegistersChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
Init() {
|
||||||
|
this._memory.clear();
|
||||||
|
this._compiler.CompileCode();
|
||||||
|
this._processCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Run() {
|
||||||
|
this.State = RunnerState.Running;
|
||||||
|
this.Init();
|
||||||
|
this.StartInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
Resume() {
|
||||||
|
this.State = RunnerState.Running;
|
||||||
|
this.StartInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
Step() {
|
||||||
|
if (this._processCounter >= this._compiler.PCToCode.size)
|
||||||
|
return;
|
||||||
|
if (this.State === RunnerState.Running) {
|
||||||
|
this.Stop();
|
||||||
|
}
|
||||||
|
this.State = RunnerState.Step;
|
||||||
|
this.RunOneLineCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop() {
|
||||||
|
this.State = RunnerState.Stop;
|
||||||
|
this.StopInterval()
|
||||||
|
}
|
||||||
|
|
||||||
|
Refresh() {
|
||||||
|
this.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetMemory(t: number): Byte {
|
||||||
|
console.log(this._memory)
|
||||||
|
return this._memory.GetVal(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetMemory(t: number, val: Byte): void {
|
||||||
|
console.log("mem Test:")
|
||||||
|
console.log(this)
|
||||||
|
console.log(this._memory)
|
||||||
|
|
||||||
|
this._memory.SetVal(t, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveEnv() {
|
||||||
|
if (this._stack.length > 8)
|
||||||
|
throw new Error("Stack overflow");
|
||||||
|
this._stack.push([this._processor.GetRegisters().map(x=>x.Copy()), this._processCounter]);
|
||||||
|
}
|
||||||
|
|
||||||
|
RestoreEnv() {
|
||||||
|
if (this._stack.length === 0) {
|
||||||
|
this.Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let [regs, pc] = this._stack.pop()!;
|
||||||
|
console.log('restore',regs);
|
||||||
|
this._processor.SetRegisters(regs);
|
||||||
|
this._processCounter = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPC(addr: number) {
|
||||||
|
this._processCounter = addr;
|
||||||
|
console.log("PC set to", addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRegisters(): Byte[] {
|
||||||
|
return this._processor.GetRegisters();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPC(): number {
|
||||||
|
return this._processCounter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Processor {
|
||||||
|
_registers: Map<string, Byte>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._registers = new Map<string, Byte>([
|
||||||
|
["AX", new Byte()],
|
||||||
|
["BX", new Byte()],
|
||||||
|
["CX", new Byte()],
|
||||||
|
["DX", new Byte()],
|
||||||
|
["EX", new Byte()],
|
||||||
|
["FX", new Byte()],
|
||||||
|
["GX", new Byte()]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static KeyWords = ["nop","ldc","add","div","and","or","xor","not","lsl","lsr","ld","st","mov","cp","jp","ltjp","gtjp","eqjp","call","ret"];
|
||||||
|
// static Registers = ["AX","BX","CX","DX","EX","FX","GX"];
|
||||||
|
GetRegister(register: string | number): Byte {
|
||||||
|
if (this._registers.has(register as string))
|
||||||
|
return this._registers.get(register as string)!;
|
||||||
|
else
|
||||||
|
throw new Error("Invalid register");
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRegisters(): Byte[] {
|
||||||
|
console.log("Get", this._registers.values());
|
||||||
|
return Array.from(this._registers.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRegisters(registers: Byte[]) {
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
console.log("Set", String.fromCharCode('A'.charCodeAt(0) + i) + 'X',registers[i].value);
|
||||||
|
this._registers.get(String.fromCharCode('A'.charCodeAt(0) + i) + 'X')!.SetValue(registers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRegisterValue(register: string | number): number {
|
||||||
|
return this.GetRegister(register).GetValue() as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRegisterValue(register: string | number, value: number) {
|
||||||
|
this.GetRegister(register).SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
InRegister(register: string | number): boolean {
|
||||||
|
return typeof register === 'string' && this._registers.has(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCode(mnemonic: string, operandA: number | string, operandB: number | string, getMemory: (addr: number) => Byte, setMemory: (addr: number, value: Byte) => void, setPC: (addr: number) => void, saveEnv: () => void, restoreEnv: () => void) {
|
||||||
|
console.log(mnemonic, operandA, operandB);
|
||||||
|
if(this.InRegister(operandA))
|
||||||
|
console.log(this.GetRegisterValue(operandA));
|
||||||
|
if(this.InRegister(operandB))
|
||||||
|
console.log(this.GetRegisterValue(operandB));
|
||||||
|
switch (mnemonic) {
|
||||||
|
case "nop":
|
||||||
|
break;
|
||||||
|
case "ldc":
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
this.GetRegister(operandA).SetValue(operandB);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "add":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).AddAndAssign(this._registers.get(operandB as string)!);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "div":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
let a = this.GetRegisterValue(operandA), b = this.GetRegisterValue(operandB);
|
||||||
|
this.SetRegisterValue(operandA, Math.floor(a / b));
|
||||||
|
this.SetRegisterValue("AX", a % b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "and":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).AndAndAssign(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "or":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).OrAndAssign(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "xor":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).XorAndAssign(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "not":
|
||||||
|
if (this.InRegister(operandA) && operandB === 0) {
|
||||||
|
this.GetRegister(operandA).NotAndAssign();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "lsl":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).LslAndAssign(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "lsr":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).LsrAndAssign(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ld":
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
this.GetRegister(operandA).SetValue(getMemory(operandB as number));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "st":
|
||||||
|
console.log(operandA, operandB);
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
setMemory(operandB as number, this.GetRegister(operandA));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "mov":
|
||||||
|
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||||
|
this.GetRegister(operandA).SetValue(this.GetRegister(operandB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
this.GetRegister(operandA).SetValue(operandB);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "cp":
|
||||||
|
console.log(operandA, operandB);
|
||||||
|
operandA = this.InRegister(operandA) ? this.GetRegisterValue(operandA) : operandA;
|
||||||
|
operandB = this.InRegister(operandB) ? this.GetRegisterValue(operandB) : operandB;
|
||||||
|
if (typeof operandA === 'number' && typeof operandB === 'number') {
|
||||||
|
console.log(operandA, operandB);
|
||||||
|
for (let i = 0; i < (this._registers.get("BX")!.GetValue() as number); i++)
|
||||||
|
setMemory(operandB + i, getMemory(operandA + i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
if (typeof operandA === 'number' && typeof operandB === 'number' && operandA === 0) {
|
||||||
|
setPC(operandB as number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ltjp":
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
if (this.GetRegisterValue("BX") < this.GetRegisterValue(operandA))
|
||||||
|
setPC(operandB as number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "gtjp":
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
if (this.GetRegisterValue("BX") > this.GetRegisterValue(operandA))
|
||||||
|
setPC(operandB as number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "eqjp":
|
||||||
|
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||||
|
if (this.GetRegisterValue("BX") === this.GetRegisterValue(operandA))
|
||||||
|
setPC(operandB as number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "call":
|
||||||
|
if (typeof operandA === 'number' && typeof operandB === 'number' && operandA === 0) {
|
||||||
|
saveEnv();
|
||||||
|
setPC(operandB as number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ret":
|
||||||
|
if (typeof operandA === 'number' && typeof operandB === 'number' && operandA === 0 && operandB === 0) {
|
||||||
|
restoreEnv();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
throw new Error("Invalid argument");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
import {LRParser} from "@lezer/lr"
|
||||||
|
export const parser = LRParser.deserialize({
|
||||||
|
version: 14,
|
||||||
|
states: "'OQQOPOOOOOO'#C_'#C_OOOO'#Ca'#CaOOOO'#Cf'#CfO!hOQO'#C^O!uOQO'#C^O#aOPO'#C^OOOO'#Co'#CoQQOPOOOOOO'#Ch'#ChO#fOPO'#CjO#kOPO'#ClO!uOQO,58xOOOO,58x,58xO#aOPO,58xO#pOPO,58xOOOO'#Cc'#CcOOOO'#Cd'#CdOOOO'#Cb'#CbO#xOPO,58xOOOO-E6m-E6mO#}OPO'#CkOOOO,59U,59UO$YOPO'#CmOOOO,59W,59WO$eOPO1G.dOOOO1G.d1G.dO#aOPO1G.dO!uOQO1G.dO$jOPO'#CpO$oOPO,59VO$zOPO'#CqO%POPO,59XO!uOQO7+$OOOOO7+$O7+$OO#pOPO7+$OOOOO,59[,59[OOOO-E6n-E6nOOOO,59],59]OOOO-E6o-E6oO#pOPO<<GjOOOO<<Gj<<GjO#aOPO<<GjOOOOAN=UAN=UO#aOPOAN=UOOOOG22pG22p",
|
||||||
|
stateData: "%[~OSPOZROgQOhQOiQOjQOkQOlQOmQOnQOoQOpQOqQOrQOsQOtQOuQOvQOwQOxQOyQOzQO~O]XO!TYO!UZO~PTOXaO{`O|`O}`O!O`O!P`O!Q`O!R`O~O]XO~OXeO~ObgO~OZRO]XO~O!SlO~O!SmOZ_X]_X~O!SoOZaX]aX~O!SqO~OXtO~O!SmOZ_a]_a~ObvO~O!SoOZaa]aa~O",
|
||||||
|
goto: "#lfPPgkPov!S!SP!YP!jP#P#S#P#VP#Y#`#fTVOWTSOWSTOWR[SQcTQi[QslRxqXbT[lqSUOWQ^SQk_QzsR|xS]SUSj^_QrkQysS{xzR}|R_SRfYRhZQWORdWQneRunQpgRwp",
|
||||||
|
nodeNames: "⚠ Program Statement Label LabelToken Mnemonic Operand Register Immediate ImmediateToken Comment CommentToken Newline NewlineToken Db NumberList Dw StringList StringToken",
|
||||||
|
maxTerm: 52,
|
||||||
|
skippedNodes: [0],
|
||||||
|
repeatNodeCount: 3,
|
||||||
|
tokenData: "E`~RrYZ#]wx#b|}#y!Q![$O!]!^$W!c!d$c!d!e%x!e!f&v!f!g't!g!h(r!h!i)p!i!j*n!j!}${#R#S${#T#U+l#U#V${#V#W.W#W#X0o#X#Y3]#Y#Z${#Z#[5]#[#^${#^#_7]#_#`${#`#a8Z#a#b<|#b#c>d#c#d@f#d#f${#f#gAd#g#hBz#h#l${#l#mCx#m#o${~#bO]~~#eTwx#t!Q![#b!c!}#b#R#S#b#T#o#b~#yOb~~$OO!S~~$TPX~!Q![$O~$]QZ~YZ$W#Q#R$WR$fV!Q![${![!]%_!c!z${!z!{%d!{!}${#R#S${#T#o${P%OT!Q![${![!]%_!c!}${#R#S${#T#o${P%dOSPR%iT{Q!Q![${![!]%_!c!}${#R#S${#T#o${R%{V!Q![${![!]%_!c!z${!z!{&b!{!}${#R#S${#T#o${R&gT|Q!Q![${![!]%_!c!}${#R#S${#T#o${R&yV!Q![${![!]%_!c!z${!z!{'`!{!}${#R#S${#T#o${R'eT}Q!Q![${![!]%_!c!}${#R#S${#T#o${R'wV!Q![${![!]%_!c!z${!z!{(^!{!}${#R#S${#T#o${R(cT!OQ!Q![${![!]%_!c!}${#R#S${#T#o${R(uV!Q![${![!]%_!c!z${!z!{)[!{!}${#R#S${#T#o${R)aT!PQ!Q![${![!]%_!c!}${#R#S${#T#o${R)sV!Q![${![!]%_!c!z${!z!{*Y!{!}${#R#S${#T#o${R*_T!QQ!Q![${![!]%_!c!}${#R#S${#T#o${R*qV!Q![${![!]%_!c!z${!z!{+W!{!}${#R#S${#T#o${R+]T!RQ!Q![${![!]%_!c!}${#R#S${#T#o${~+oX!Q![${![!]%_!c!}${#R#S${#T#W${#W#X,[#X#b${#b#c-Y#c#o${~,_V!Q![${![!]%_!c!}${#R#S${#T#W${#W#X,t#X#o${~,yTi~!Q![${![!]%_!c!}${#R#S${#T#o${~-]V!Q![${![!]%_!c!}${#R#S${#T#W${#W#X-r#X#o${~-wTk~!Q![${![!]%_!c!}${#R#S${#T#o${~.ZW!Q![${![!]%_!c!}${#R#S${#T#U.s#U#d${#d#e0Z#e#o${~.vV!Q![${![!]%_!c!}${#R#S${#T#`${#`#a/]#a#o${~/`V!Q![${![!]%_!c!}${#R#S${#T#`${#`#a/u#a#o${~/zTy~!Q![${![!]%_!c!}${#R#S${#T#o${~0`Tt~!Q![${![!]%_!c!}${#R#S${#T#o${~0rZ!Q![${![!]%_!c!}${#R#S${#T#U${#U#V1e#V#]${#]#^1y#^#k${#k#l2w#l#o${R1jT!TQ!Q![${![!]%_!c!}${#R#S${#T#o${~1|V!Q![${![!]%_!c!}${#R#S${#T#j${#j#k2c#k#o${~2hTj~!Q![${![!]%_!c!}${#R#S${#T#o${R2|T!UQ!Q![${![!]%_!c!}${#R#S${#T#o${~3`V!Q![${![!]%_!c!}${#R#S${#T#e${#e#f3u#f#o${~3xV!Q![${![!]%_!c!}${#R#S${#T#^${#^#_4_#_#o${~4bV!Q![${![!]%_!c!}${#R#S${#T#d${#d#e4w#e#o${~4|Tx~!Q![${![!]%_!c!}${#R#S${#T#o${~5`V!Q![${![!]%_!c!}${#R#S${#T#h${#h#i5u#i#o${~5xV!Q![${![!]%_!c!}${#R#S${#T#^${#^#_6_#_#o${~6bV!Q![${![!]%_!c!}${#R#S${#T#d${#d#e6w#e#o${~6|Tw~!Q![${![!]%_!c!}${#R#S${#T#o${~7`V!Q![${![!]%_!c!}${#R#S${#T#d${#d#e7u#e#o${~7zTu~!Q![${![!]%_!c!}${#R#S${#T#o${~8^Y!Q![${![!]%_!c!}${#R#S${#T#W${#W#X8|#X#g${#g#h9|#h#i;f#i#o${~9RVq~!Q![${![!]%_!c!}${#R#S${#T#V${#V#W9h#W#o${~9mTh~!Q![${![!]%_!c!}${#R#S${#T#o${~:PX!Q![${![!]%_!c!}${#R#S${#T#`${#`#a:l#a#f${#f#g;Q#g#o${~:qTo~!Q![${![!]%_!c!}${#R#S${#T#o${~;VTp~!Q![${![!]%_!c!}${#R#S${#T#o${~;iV!Q![${![!]%_!c!}${#R#S${#T#^${#^#_<O#_#o${~<RV!Q![${![!]%_!c!}${#R#S${#T#d${#d#e<h#e#o${~<mTv~!Q![${![!]%_!c!}${#R#S${#T#o${~=PV!Q![${![!]%_!c!}${#R#S${#T#c${#c#d=f#d#o${~=iV!Q![${![!]%_!c!}${#R#S${#T#j${#j#k>O#k#o${~>TTs~!Q![${![!]%_!c!}${#R#S${#T#o${~>gV!Q![${![!]%_!c!}${#R#S${#T#c${#c#d>|#d#o${~?PX!Q![${![!]%_!c!}${#R#S${#T#d${#d#e?l#e#h${#h#i@Q#i#o${~?qTg~!Q![${![!]%_!c!}${#R#S${#T#o${~@VTn~!Q![${![!]%_!c!}${#R#S${#T#o${~@iV!Q![${![!]%_!c!}${#R#S${#T#f${#f#gAO#g#o${~ATTl~!Q![${![!]%_!c!}${#R#S${#T#o${~AgV!Q![${![!]%_!c!}${#R#S${#T#X${#X#YA|#Y#o${~BPV!Q![${![!]%_!c!}${#R#S${#T#h${#h#iBf#i#o${~BkTz~!Q![${![!]%_!c!}${#R#S${#T#o${~B}V!Q![${![!]%_!c!}${#R#S${#T#h${#h#iCd#i#o${~CiTr~!Q![${![!]%_!c!}${#R#S${#T#o${~C{V!Q![${![!]%_!c!}${#R#S${#T#c${#c#dDb#d#o${~DeV!Q![${![!]%_!c!}${#R#S${#T#f${#f#gDz#g#o${~EPTm~!Q![${![!]%_!c!}${#R#S${#T#o${",
|
||||||
|
tokenizers: [0, 1],
|
||||||
|
topRules: {"Program":[0,1]},
|
||||||
|
tokenPrec: 0
|
||||||
|
})
|
|
@ -0,0 +1,20 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
export const
|
||||||
|
Program = 1,
|
||||||
|
Statement = 2,
|
||||||
|
Label = 3,
|
||||||
|
LabelToken = 4,
|
||||||
|
Mnemonic = 5,
|
||||||
|
Operand = 6,
|
||||||
|
Register = 7,
|
||||||
|
Immediate = 8,
|
||||||
|
ImmediateToken = 9,
|
||||||
|
Comment = 10,
|
||||||
|
CommentToken = 11,
|
||||||
|
Newline = 12,
|
||||||
|
NewlineToken = 13,
|
||||||
|
Db = 14,
|
||||||
|
NumberList = 15,
|
||||||
|
Dw = 16,
|
||||||
|
StringList = 17,
|
||||||
|
StringToken = 18
|
|
@ -0,0 +1,6 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
declare module '*.vue' {
|
||||||
|
import { ComponentOptions } from 'vue'
|
||||||
|
const componentOptions: ComponentOptions
|
||||||
|
export default componentOptions
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
})
|
Loading…
Reference in New Issue