[feature] 更新运行速度设置,机器码旁增加asm 优化布局 等等
This commit is contained in:
parent
ad2916b018
commit
087d1fe8d6
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -1,18 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import {Runner, RunnerState} from "../utils/Runner.ts";
|
||||
import {inject} from "vue";
|
||||
import {inject, ref} from "vue";
|
||||
import {Compiler} from "../utils/Complier.ts";
|
||||
let runner:Runner = inject("runner") as Runner;
|
||||
console.log(runner);
|
||||
|
||||
let runner: Runner = inject("runner") as Runner;
|
||||
let compiler = inject("compiler") as Compiler;
|
||||
let info = ref("Ready");
|
||||
let speed = ref(20);
|
||||
let ok = ref(true);
|
||||
compiler.AddEventListener("CompileError", (errorType, e) => {
|
||||
info.value = errorType + " " + (e.index + 1) + ": " + e.message;
|
||||
ok.value = false;
|
||||
});
|
||||
runner.AddEventListener("RunningError", (errorType, e) => {
|
||||
// pc 指向下一条指令 所以这里不用+1 MachineCode 从0开始,所以需要-1
|
||||
info.value = errorType + " " + (e.index - 1) + ": " + e.message;
|
||||
ok.value = false;
|
||||
});
|
||||
|
||||
|
||||
function compileCode() {
|
||||
runner.Stop();
|
||||
compiler.CompileCode();
|
||||
}
|
||||
|
||||
function startOrContinue() {
|
||||
switch (runner.State)
|
||||
{
|
||||
switch (runner.State) {
|
||||
case RunnerState.Running:
|
||||
break;
|
||||
case RunnerState.Step:
|
||||
|
@ -23,16 +37,22 @@ function startOrContinue() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
runner.Pause();
|
||||
}
|
||||
|
||||
function step() {
|
||||
if(compiler.CodeChanged)
|
||||
{
|
||||
if (compiler.CodeChanged) {
|
||||
compiler.CompileCode();
|
||||
}
|
||||
runner.Step();
|
||||
}
|
||||
|
||||
function stop() {
|
||||
runner.Stop();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
runner.Refresh();
|
||||
}
|
||||
|
@ -40,10 +60,21 @@ function refresh() {
|
|||
|
||||
<template>
|
||||
<div id="controlPanel">
|
||||
<div></div>
|
||||
<div id="buttons">
|
||||
<div>
|
||||
<div class="info-bar">
|
||||
<img class="type" :class="{invisible : ok}" src="../assets/error.png" alt="error"/>
|
||||
<img class="type" :class="{invisible : !ok}" src="../assets/ok.png" alt="ok"/>
|
||||
<span class="info">{{ info }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="speed">
|
||||
<input class="speed-input" v-model="speed" type="number" min="0" @input="runner.RunningSpeed = speed"/>
|
||||
<span class="speed-text">ms/step</span>
|
||||
</div>
|
||||
<div class="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/pause.png" alt="暂停" @click="pause">
|
||||
<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">
|
||||
|
@ -59,18 +90,67 @@ function refresh() {
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#controlPanel > :first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#controlPanel > :last-child {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
#buttons{
|
||||
.info-bar {
|
||||
font-size: 19px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
padding: 5px 5px 5px 20px;
|
||||
}
|
||||
|
||||
.type {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0 3px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button{
|
||||
.speed {
|
||||
padding-right: 25px;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.speed-input {
|
||||
display: table-cell;
|
||||
height: 27px;
|
||||
border: 1px #373737 solid;
|
||||
border-right-width: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.speed-input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.speed-text {
|
||||
height: 21px;
|
||||
width: 70px;
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
background-color: #373737;
|
||||
}
|
||||
|
||||
.button {
|
||||
flex: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
@ -78,9 +158,9 @@ function refresh() {
|
|||
padding: 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
|
||||
}
|
||||
.button:hover{
|
||||
|
||||
.button:hover {
|
||||
background-color: #555555;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,15 +7,25 @@ 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 asmCode: 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)];
|
||||
});
|
||||
asmCode.value = Array.from(compiler.PCToCode.values()).map((x) => {
|
||||
return x.split(/[\s,]+/).map((x) => {
|
||||
x = x.trim();
|
||||
if (compiler.Labels.has(x)) {
|
||||
return x + '(' + compiler.Labels.get(x)!.toString(10).padStart(2, '0') + ')';
|
||||
}
|
||||
return x;
|
||||
});
|
||||
})
|
||||
})
|
||||
runner.AddEventListener("PCChanged", (_) => {
|
||||
currentPC.value = runner.State===RunnerState.Stop?-1: runner._processCounter;
|
||||
currentPC.value = runner._processCounter - 1;
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -25,11 +35,15 @@ runner.AddEventListener("PCChanged", (_) => {
|
|||
<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>
|
||||
<span class="line-number">{{ index }}</span>
|
||||
<div class="gutter"></div>
|
||||
<span class="mnemonic">{{ instruction[0] }}</span>
|
||||
<span class="operand">{{ instruction[1] }}</span>
|
||||
<span class="operand">{{ instruction[2] }}</span>
|
||||
<div class="split"></div>
|
||||
<span class="asm-mnemonic">{{ asmCode[index][0] }}</span>
|
||||
<span class="asm-operand">{{ asmCode[index][1] }},</span>
|
||||
<span class="asm-operand">{{ asmCode[index][2] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,10 +95,31 @@ span {
|
|||
|
||||
.operand {
|
||||
color: rgb(224, 108, 117);
|
||||
|
||||
}
|
||||
|
||||
.gutter {
|
||||
display: inline;
|
||||
width: 9px;
|
||||
}
|
||||
|
||||
.split {
|
||||
margin-left: 10px;
|
||||
background-color: #9e9e9e;
|
||||
width: 1px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.asm-mnemonic {
|
||||
color: rgb(198, 120, 221);
|
||||
margin-left: 10px;
|
||||
padding-right: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.asm-operand {
|
||||
color: rgb(224, 108, 117);
|
||||
margin-left: 5px;
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
|
@ -96,7 +96,7 @@ let memory = computed(() => {
|
|||
}
|
||||
|
||||
.register-loose-layout {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
|
||||
.register-compact-layout {
|
||||
|
@ -150,6 +150,7 @@ span {
|
|||
|
||||
.screen-row {
|
||||
color: rgb(224, 188, 105);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.screen-memory {
|
||||
|
|
|
@ -5,7 +5,6 @@ export class CompileError extends Error {
|
|||
super(message);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
index: number;
|
||||
}
|
||||
|
||||
|
@ -204,6 +203,8 @@ export class Compiler {
|
|||
mnemonicAndVals[i] = this.Labels.get(mnemonicAndVals[i])!.toString(16).padStart(2,'0');
|
||||
else
|
||||
mnemonicAndVals[i] = parseInt(mnemonicAndVals[i]).toString(16).padStart(2,'0');
|
||||
if (mnemonicAndVals[1] === 'NaN'||mnemonicAndVals[2] === 'NaN')
|
||||
throw new CompileError("Invalid operand", index);
|
||||
this.PCToMachineCode.set(pc, Compiler.KeyWordsToMachineCode.get(mnemonicAndVals[0])! + mnemonicAndVals[1] + mnemonicAndVals[2]);
|
||||
});
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
import {MemoryManager, Byte} from "./MemoryManager";
|
||||
import {Compiler} from "./Complier.ts";
|
||||
|
||||
export class RunningError extends Error {
|
||||
constructor(message: string, index: number) {
|
||||
super(message);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
index: number;
|
||||
}
|
||||
|
||||
export enum RunnerState {
|
||||
Stop,
|
||||
Running,
|
||||
|
@ -9,7 +18,7 @@ export enum RunnerState {
|
|||
|
||||
export class Runner {
|
||||
// 与Compiler 类似的事件机制 要是再来一个我就把这段逻辑单独抽出来
|
||||
//[PCChanged,RegistersChanged]
|
||||
//[PCChanged,RegistersChanged,RunningError(string,RunningError)]
|
||||
_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) {
|
||||
|
@ -28,6 +37,7 @@ export class Runner {
|
|||
}
|
||||
|
||||
State: RunnerState = RunnerState.Stop;
|
||||
_runningSpeed: number = 20;
|
||||
_processor: Processor;
|
||||
_memory: MemoryManager;
|
||||
_processCounter: number = 0;
|
||||
|
@ -35,6 +45,18 @@ export class Runner {
|
|||
_stack: [Byte[], number][] = [];
|
||||
_intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
set RunningSpeed(speed: number) {
|
||||
this._runningSpeed = speed;
|
||||
if (this.State === RunnerState.Running) {
|
||||
this.Pause();
|
||||
this.Resume();
|
||||
}
|
||||
}
|
||||
|
||||
get RunningSpeed() {
|
||||
return this._runningSpeed;
|
||||
}
|
||||
|
||||
StartInterval() {
|
||||
if (this._intervalId === null)
|
||||
this._intervalId = setInterval(() => {
|
||||
|
@ -43,7 +65,7 @@ export class Runner {
|
|||
return;
|
||||
}
|
||||
this.RunOneLineCode();
|
||||
}, 20);
|
||||
}, this._runningSpeed);
|
||||
}
|
||||
|
||||
StopInterval() {
|
||||
|
@ -73,30 +95,44 @@ export class Runner {
|
|||
if (this._compiler.PCToCode.has(this._processCounter))
|
||||
code = this._compiler.PCToCode.get(this._processCounter) as string;
|
||||
else
|
||||
throw new Error("PC out of range");
|
||||
throw new RunningError("PC out of range", 0);
|
||||
this._processCounter++;
|
||||
let mnemonicAndVals = code.split(/[\s,]+/);
|
||||
// 如果指令不合法 Compiler应该会有异常抛出 则不能执行到这里
|
||||
if (mnemonicAndVals.length !== 3)
|
||||
throw new Error("Invalid code");
|
||||
throw new RunningError("Invalid code", this._processCounter);
|
||||
let mnemonic = mnemonicAndVals[0];
|
||||
function getVal(runner:Runner, val: string) {
|
||||
|
||||
function getVal(runner: Runner, val: string) {
|
||||
if (runner._processor._registers.has(val))
|
||||
return val;
|
||||
else if(runner._compiler.Labels.has(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");
|
||||
if (isNaN(t)) {
|
||||
throw new RunningError("Invalid argument", runner._processCounter);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return [mnemonic, getVal(this,mnemonicAndVals[1]), getVal(this,mnemonicAndVals[2])];
|
||||
|
||||
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);
|
||||
try {
|
||||
this._processor.RunCode(...this.CommandParsing(), this.GetMemory, this.SetMemory, this.SetPC, this.SaveEnv, this.RestoreEnv);
|
||||
}catch (e:any)
|
||||
{
|
||||
console.log(e);
|
||||
e = e as RunningError;
|
||||
if(e === undefined)
|
||||
throw new RunningError("Unknown error",this._processCounter);
|
||||
e.index = e.index === -1? this._processCounter : e.index;
|
||||
this.Pause();
|
||||
this.Notify("RunningError", e);
|
||||
}
|
||||
this.Notify("PCChanged");
|
||||
this.Notify("RegistersChanged");
|
||||
}
|
||||
|
@ -128,9 +164,16 @@ export class Runner {
|
|||
this.RunOneLineCode()
|
||||
}
|
||||
|
||||
Pause() {
|
||||
this.State = RunnerState.Stop;
|
||||
this.StopInterval()
|
||||
}
|
||||
|
||||
Stop() {
|
||||
this.State = RunnerState.Stop;
|
||||
this.StopInterval()
|
||||
this._processCounter = 0;
|
||||
this._memory.clear();
|
||||
}
|
||||
|
||||
Refresh() {
|
||||
|
@ -138,38 +181,31 @@ export class Runner {
|
|||
}
|
||||
|
||||
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]);
|
||||
throw new RunningError("Stack overflow",this._processCounter);
|
||||
this._stack.push([this._processor.GetRegisters().map(x => x.Copy()), this._processCounter]);
|
||||
}
|
||||
|
||||
RestoreEnv() {
|
||||
if (this._stack.length === 0) {
|
||||
this.Stop();
|
||||
this.Pause();
|
||||
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[] {
|
||||
|
@ -202,17 +238,15 @@ class Processor {
|
|||
if (this._registers.has(register as string))
|
||||
return this._registers.get(register as string)!;
|
||||
else
|
||||
throw new Error("Invalid register");
|
||||
throw new RunningError("Invalid register",-1);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +256,12 @@ class Processor {
|
|||
}
|
||||
|
||||
SetRegisterValue(register: string | number, value: number) {
|
||||
this.GetRegister(register).SetValue(value);
|
||||
try {
|
||||
this.GetRegister(register).SetValue(value);
|
||||
}catch (e)
|
||||
{
|
||||
throw new RunningError("Invalid value",-1);
|
||||
}
|
||||
}
|
||||
|
||||
InRegister(register: string | number): boolean {
|
||||
|
@ -230,11 +269,15 @@ class Processor {
|
|||
}
|
||||
|
||||
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));
|
||||
{
|
||||
let a = operandA;
|
||||
let b = operandB;
|
||||
if (this.InRegister(a))
|
||||
a = a + '(' + this.GetRegisterValue(a) + ')';
|
||||
if (this.InRegister(b))
|
||||
b = b + '(' + this.GetRegisterValue(b) + ')';
|
||||
console.log(mnemonic,a,b);
|
||||
}
|
||||
switch (mnemonic) {
|
||||
case "nop":
|
||||
break;
|
||||
|
@ -301,7 +344,6 @@ class Processor {
|
|||
}
|
||||
break;
|
||||
case "st":
|
||||
console.log(operandA, operandB);
|
||||
if (this.InRegister(operandA) && typeof operandB === 'number') {
|
||||
setMemory(operandB as number, this.GetRegister(operandA));
|
||||
return;
|
||||
|
@ -311,18 +353,15 @@ class Processor {
|
|||
if (this.InRegister(operandA) && this.InRegister(operandB)) {
|
||||
this.GetRegister(operandA).SetValue(this.GetRegister(operandB));
|
||||
return;
|
||||
}
|
||||
else if(this.InRegister(operandA) && typeof operandB === 'number') {
|
||||
} 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;
|
||||
|
@ -368,7 +407,9 @@ class Processor {
|
|||
return;
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new RunningError("Invalid mnemonic",-1);
|
||||
}
|
||||
throw new Error("Invalid argument");
|
||||
throw new RunningError("Invalid argument",-1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue