TokenQue2/src/utils/Complier.ts

218 lines
11 KiB
TypeScript
Raw Normal View History

2024-11-19 21:09:20 +08:00
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: 如果栈为空7k条指令
*/
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');
if (mnemonicAndVals[1] === 'NaN'||mnemonicAndVals[2] === 'NaN')
throw new CompileError("Invalid operand", index);
2024-11-19 21:09:20 +08:00
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");
}
}