TokenQue2/src/utils/Complier.ts

218 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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');
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) {
if (e instanceof CompileError)
this.Notify("CompileError", e);
throw e;
}
this.Notify("CompileFinished");
}
}