[feature] 更新运行速度设置,机器码旁增加asm 优化布局 等等

This commit is contained in:
lichx 2024-11-22 22:33:18 +08:00
parent ad2916b018
commit 087d1fe8d6
8 changed files with 210 additions and 52 deletions

BIN
src/assets/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/ok.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -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;
}

View File

@ -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>

View File

@ -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 {

View File

@ -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) {

View File

@ -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);
}
}