陳鍾誠 | 教材 | 程式 | 文章 | 留言版


如何自己設計解譯器

如何自己設計解譯器


作者:陳鍾誠

摘要


  • 程式下載:Interpreter.zip

  • // 共有兩個檔案
    // 程式檔 : SicAssembler.cs
    // 測試檔 : SUM.sic
    // ------------------------------ SicAssembler.cs ------------------------------------
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Text.RegularExpressions;
    using Microsoft.VisualBasic;
    
    namespace SICAssembler { public class SIC { static void Main(string[] args) { SicAssembler asm = new SicAssembler(); asm.parse(args[0]); asm.pass2(); SicMemory memory = new SicMemory(); SicLoader.load(asm.codes, memory); SicVirtualMachine vm = new SicVirtualMachine(memory, 0); vm.run(); } }
    public class SicVirtualMachine { public static OpTable opTable = new OpTable(); SicMemory mem; int A = 0, X = 0, L = 0, SW = 0, PC = 0; public SicVirtualMachine(SicMemory pMemory, int pCounter) { mem = pMemory; PC = pCounter; }
    public void dump() { Debug.log("A=" + A + " X=" + X + " L=" + L + " SW=" + SW + " PC=" + PC); }
    public void run() { while (runCode()) { } dump(); }
    public int compare(int a, int b) { if (a > b) return 1; else if (a < b) return -1; else return 0; }
    public bool runCode() { byte op = mem.memory[PC]; byte a1 = mem.memory[PC + 1]; byte a2 = mem.memory[PC + 2]; int m = ((a1 & 0x7F) << 8) + a2; int word = mem.getWord(m); String opStr = String.Format("{0:X2}", op); Debug.log(opTable.codeToOp[opStr]+"\tmem("+String.Format("{0:X2}", m)+")\t= "+word); switch (op) { case 0x18: // ADD m A += word; break; case 0x40: // AND m A &= word; break; case 0x28: // COMP m SW = compare(A, word); break; case 0x24: // DIV m A /= word; break; case 0x3C: // J m PC = m; break; case 0x30: // JEQ m if (SW == 0) PC = m; break; case 0x34: // JGT m if (SW > 0) PC = m; break; case 0x38: // JLT m if (SW < 0) PC = m; break; case 0x48: // JSUB m L = PC; PC = m; break; case 0x00: // LDA m A = word; break; case 0x50: // LDCH m A = (A & 0xFF0) | mem.memory[m]; break; case 0x04: // LDX m X = word; break; case 0x20: // MUL m A *= word; break; case 0x44: // OR m A |= word; break; // case 0xD8: // RD m case 0x4C: // RSUB if (L == 0) return false; PC = L; break; case 0x0C: // STA m mem.setWord(A, m); break; case 0x54: // STCH m mem.memory[m] = (byte)A; break; case 0x10: // STX m mem.setWord(X, m); break; case 0x1C: // SUB m A -= word; break; // case 0xB0: // SVC x // case 0xE0: // TD m // break; case 0x2C: // TIX m X++; SW = compare(X, word); break; // case 0xDC: // WD m // break; default: Debug.log(String.Format("Error : op={0:X2} not found !", op)); break; } PC += 3; return true; } }
    public class SicLoader { public static void load(CodeList codes, SicMemory m) { foreach (Code code in codes) { for (int i = 0; i < code.objCode.Length; i+=2) { String hex = code.objCode; int offset = code.offset + i/2; byte b = Byte.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier); m.memory[offset] = b; Debug.log(String.Format("{0:X2}:{1:X2}", offset, b)); } } } }
    public class SicMemory { public byte[] memory = new byte[32768];
    public SicMemory() { for (int i = 0; i < memory.Length; i++) memory[i] = 0; }
    public void setWord(int word, int offset) { memory[offset] = (byte)(word >> 16); memory[offset + 1] = (byte)(word >> 8); memory[offset + 2] = (byte)(word); }
    public int getWord(int offset) { return memory[offset] << 16 | memory[offset + 1] << 8 | memory[offset + 2]; } }
    public class SicAssembler { public static OpTable opTable = new OpTable(); public CodeList codes = new CodeList(); public SymbolTable symbolTable = new SymbolTable();
    public void parse(String pFileName) { Debug.log("======================= PARSE ================================"); String text = IO.fileToText(pFileName); text = text.Replace('\r', ' '); String[] lines = text.Split('\n'); int offset = 0; for (int i = 0; i < lines.Length; i++) { String line = lines[i]; if (line.Trim().Length == 0) continue; if (line.StartsWith(".")) continue; Code code = new Code(line, opTable); code.lineNumber = i; if (code.offset >= 0) offset = code.offset; else code.offset = offset; Debug.log(code.ToString()); if (code.label.Length > 0) symbolTable.Add(code.label, code); codes.Add(code); offset = offset + code.codeSize; } }
    public void pass2() { Debug.log("======================= PASS2 ================================"); for (int i = 0; i < codes.Count; i++) { Code code = codes[i]; if (!code.isPseudo) { if (code.arg.Length == 0) code.objCode += "0000"; else { if (symbolTable.ContainsKey(code.arg)) { int argCode = symbolTable[code.arg].offset; if (code.x == 'X') argCode += 0x8000; code.objCode += String.Format("{0:X4}", argCode); } else Debug.error("ARG " + code.arg + " not found !"); } } Debug.log(code.ToString()); } } }
    public class OpTable { public Dictionary<String, String> opToCode = new Dictionary<String, String>(); public Dictionary<String, String> codeToOp = new Dictionary<String, String>(); public static String opCodes = "ADD=18,ADDF=58,ADDR=90,AND=40,CLEAR=B4,COMP=28,COMPF=88,DIV=24,DIVF=64,DIVR=9C,FIX=C4," + "FLOAT=C0,HIO=F4,J=3C,JEQ=30,JGT=34,JLT=38,JSUB=48,LDA=00,LDB=68,LDCH=50,LDF=70,LDL=08," + "LDS=6C,LDT=74,LDX=04,LPS=D0,MUL=20,MULF=60,MULR=98,NORM=C8,OR=44,RD=D8,RMO=AC,RSUB=4C," + "SHIFTL=A4,SHIFTR=A8,SIO=F0,SSK=EC,STA=0C,STB=78,STCH=54,STF=80,STI=D4,STL=14,STS=7C,STSW=E8," + "STT=84,STX=10,SUB=1C,SUBF=5C,SUBR=94,SVC=B0,TD=E0,TIO=F8,TIX=2C,TIXR=B8,WD=DC," + "RESB=,RESW=,BYTE=,WORD=,START=,END="; public OpTable() { Debug.log("======================= OP TABLE ================================"); String[] records = opCodes.Split(','); for (int i = 0; i < records.Length; i++) { String[] tokens = records[i].Split('='); opToCode.Add(tokens[0], tokens[1]); if (tokens.Length > 1 && tokens[1].Length>0) codeToOp.Add(tokens[1], tokens[0]); Debug.log(tokens[0] + "\t" + tokens[1]); } } }
    public class SymbolTable : Dictionary<String, Code> { }
    public class CodeList : List<Code> { }
    public class Code { public static String PSEUDO_OP = ",RESB,RESW,BYTE,WORD,START,END,"; public String label = "", op = "", arg = ""; public char x = ' ';
    public String objCode = null; public int offset = -1; public int codeSize = 0; public int lineNumber = 0; public bool isPseudo;
    public Code(String line, OpTable opTable) { String[] tokens = line.Split('\t');
    int argIdx; if (opTable.opToCode.ContainsKey(tokens[0])) { op = tokens[0].Trim(); argIdx = 1; } else { label = tokens[0].Trim(); op = tokens[1].Trim(); argIdx = 2; } if (argIdx < tokens.Length) { String argStr = tokens[argIdx]; String[] args = argStr.Split(','); arg = args[0].Trim(); if (args.Length > 1) if (args[1].Trim().Equals("X")) x = 'X'; } objCode = opTable.opToCode[op]; if (objCode == null) Debug.error("op:" + op + " not found!"); if (op.Equals("BYTE")) { if (arg.StartsWith("C'")) // EOF BYTE C'EOF' { String str = arg.Substring(2, arg.Length - 3); codeSize = str.Length; objCode = ""; for (int si = 0; si < str.Length; si++) { char ch = str[si]; int chInt = ch; objCode += String.Format("{0:x2}", (uint) System.Convert.ToUInt32(chInt.ToString())); } objCode = objCode.ToUpper(); } else if (arg.StartsWith("X'")) // OUTPUT BYTE X'05' { String str = arg.Substring(2, arg.Length - 3); objCode = str; codeSize = str.Length / 2; } else // THREE BYTE 3 codeSize = 1; } else if (op.Equals("WORD")) // FIVE WORD 5 { codeSize = 3; objCode = String.Format("{0:X6}", Int32.Parse(arg)); } else if (op.Equals("RESB")) // BUFFER RESB 1024 codeSize = Int32.Parse(arg) * 1; else if (op.Equals("RESW")) // LENGTH RESW 1 codeSize = Int32.Parse(arg) * 3; else if (op.Equals("START")) // COPY START 1000 offset = Int32.Parse(arg, System.Globalization.NumberStyles.AllowHexSpecifier); else if (op.Equals("END")) codeSize = 0; else codeSize = 3; // SIC 中一個指令佔 3 byte isPseudo = (PSEUDO_OP.IndexOf(","+op+",") >= 0); }
    public override String ToString() { return lineNumber + "\t" + String.Format("{0:X}", offset) + "\t" + label + "\t" + op + "\t" + arg + "\t" + x+"\t"+objCode; } }
    public class IO { // 讀取文字檔,以字串形式傳回。 public static String fileToText(String filePath) { StreamReader file = new StreamReader(filePath); String text = file.ReadToEnd(); file.Close(); return text; } }
    class Debug { public static void error(String msg) { Debug.log(msg); throw new Exception(msg); }
    public static void log(String msg) { Console.WriteLine(msg); } } }

    .--------------  SUM.sic -----------------------
    SUMPROG	START	0
    LOOP	LDA	I	. i++
    	ADD	ONE
    	STA	I
    	ADD	SUM	. sum += i
    	STA	SUM
    	LDA	I
    	COMP 	N	. if (i < N) goto LOOP
    	JLT 	LOOP 
    	LDA	SUM
    	RSUB
    I	WORD	0
    SUM	WORD	0
    N	WORD	10
    ONE	WORD	1
    	END	SUMPROG
    



    作者:陳鍾誠 E-mail:ccc@kmit.edu.tw
    Creative Commons License

    本著作係採用創用 CC 「姓名標示─相同方式分享 2.5 台灣版」授權條款釋出。

    大學課程網 | 手機入口網