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 void)[]> = new Map 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 = new Map([ ["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 = new Map([ ["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 = new Map(); PCToCode: Map = new Map(); PCToMachineCode: Map = new Map(); Labels: Map = new Map(); 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"); } }