override internal bool Compile(string filePath) { this.filePath = filePath; tokens = new List <Token>(); labels = new List <Label>(); List <FeedMe> feedMes = new List <FeedMe>(); List <StringData> stringData = new List <StringData>(); Format format = Format.Flat; bool isDataWritten = false; LexerParser.LexerParse(filePath, tokens); outputFilePath = Path.ChangeExtension(filePath, ".bin"); bool AddError(string message, Token token) { outputMessages.Add(new OutputMessage { type = OutputMessage.MessageType.Error, message = message, token = token, filename = filePath, }); return(false); } Token GetNextToken() { iT++; Token tok = tokens[iT]; return(tok); } void RequireToken(string token) { Token tok = GetNextToken(); if (tok.token != token) { ThrowError("Expected “" + token + "”, got: “" + tok.token + "”"); } } void RequireEitherToken(params string[] accepts) { Token tok = GetNextToken(); if (Array.IndexOf(accepts, tok.token) == -1) { ThrowError("Expected “" + accepts[0] + "”, got: “" + tok.token + "”"); } } Side RequireSide(byte width) { Side side = new Side(); if (PeekNext() == "[") { GetNextToken(); side.isMemoryAccess = true; } Token next = GetNextToken(); if (TryAddLabel(next, out side.offset, width, out side.feedMe)) { } else if (TryAddStringLit(next, out side.offset, width, out side.feedMe)) { } else if (TryIntLiteralParse(next.token, out side.offset)) { } else if (TryRegister(next, out side.reg, out side.width)) { } else { side.offset = (uint)ConstMathParse(next); // ThrowError("Expected register, label or literal, got: “" + next.token + "”"); } if (side.isMemoryAccess) { if (PeekNext() == "+") { GetNextToken(); next = GetNextToken(); if (TryIntLiteralParse(next.token, out side.offset) == false) { ThrowError("Expected offset (integer literal), got: “" + next.token + "”"); } } RequireToken("]"); } return(side); } bool ThrowError(string message, Token token = null) { if (token == null && iT < tokens.Count) { token = tokens[iT]; } throw new CompilerException(message, token, filePath); } UInt32 RequireLiteral(byte byteLen) { Token tok = GetNextToken(); FeedMe feedMe; UInt32 ret; if (TryAddLabel(tok, out ret, byteLen, out feedMe)) { return(ret); } else if (TryAddStringLit(tok, out ret, byteLen, out feedMe)) { return(ret); } else { UInt32 res; if (TryIntLiteralParse(tok.token, out res) == false) { ThrowError("Expected numerical or string literal, got: “" + tok.token + "”"); } // @TODO check size (byteLen) return(res); } } string RequireStringLiteral() { Token tok = GetNextToken(); if (tok.token.Length > 1 && ((tok.token[0] == '"' && tok.token[tok.token.Length - 1] == '"') || (tok.token[0] == '\'' && tok.token[tok.token.Length - 1] == '\''))) { string str = tok.token.Substring(1, tok.token.Length - 2); return(str); } ThrowError("Expected string literal, got: " + tok.token); return(null); } UInt32 RequireIntegerLiteral() { Token tok = GetNextToken(); UInt32 res; if (TryIntLiteralParse(tok.token, out res) == false) { ThrowError("Expected integer literal, got: “" + tok.token + "”"); } return(res); } UInt32 RequireLabel(int byteLen, bool absolute = true) { Token tok = GetNextToken(); if (TryLabel(tok) == false) { ThrowError($"Expected label, got “{tok.token}”"); } AddLabelRef(tok, byteLen, absolute, out FeedMe feedMe); return((UInt32)Placeholders.Label); } Registers RequireRegister(out byte width) { Registers reg; Token next = GetNextToken(); if (TryRegister(next, out reg, out width) == false) { ThrowError("Expected register, got: " + next.token); } return(reg); } bool TryRegister(Token next, out Registers reg, out byte width) { string str = next.token; if (str[0] == '%') { str = str.Substring(1); } width = 0; if (Enum.TryParse(str, true, out reg) == false || Enum.IsDefined(typeof(Registers), reg) == false) { return(false); } switch (reg) { case Registers.Al: case Registers.Cl: case Registers.Bl: case Registers.Dl: case Registers.Ah: case Registers.Ch: case Registers.Dh: case Registers.Bh: width = 1; break; case Registers.Ax: case Registers.Cx: case Registers.Dx: case Registers.Bx: case Registers.Sp: case Registers.Bp: case Registers.Si: case Registers.Di: width = 2; break; case Registers.Eax: case Registers.Ecx: case Registers.Edx: case Registers.Ebx: case Registers.Esp: case Registers.Ebp: case Registers.Esi: case Registers.Edi: width = 4; break; } return(true); } string GetNext() { iT++; Token tok = tokens[iT]; return(tok.token); } string PeekNext() { if (iT + 1 >= tokens.Count) { return(null); } Token tok = tokens[iT + 1]; return(tok.token); } bool TryStringLit(Token tok, out string str) { if (tok.token.Length > 1 && ((tok.token[0] == '"' && tok.token[tok.token.Length - 1] == '"') || (tok.token[0] == '\'' && tok.token[tok.token.Length - 1] == '\''))) { str = tok.token.Substring(1, tok.token.Length - 2); return(true); } str = null; return(false); } bool TryAddStringLit(Token tok, out UInt32 val, byte byteLen, out FeedMe feedMe) { if (TryStringLit(tok, out string str)) { StringData strData = new StringData { str = str, }; stringData.Add(strData); feedMe = new FeedMe { /*symbol = str,*/ bytePos = writer.BaseStream.Position, refType = RefType.String, refObj = strData, width = byteLen, offset = address, }; feedMes.Add(feedMe); val = (UInt32)Placeholders.String; return(true); } feedMe = null; val = 0; return(false); } bool TryLabel(Token tok) { if (tok.token[0] == '.') { return(true); } return(false); } bool TryAddLabel(Token tok, out UInt32 val, byte byteLen, out FeedMe feedMe) { if (TryLabel(tok)) { val = AddLabelRef(tok, byteLen, true, out feedMe); return(true); } feedMe = null; val = 0; return(false); } void Write(UInt32 ii, int writeWidth) { switch (writeWidth) { case 4: writer.Write((UInt32)ii); break; case 2: writer.Write((UInt16)ii); break; case 1: writer.Write((byte)ii); break; default: ThrowError("Compiler error: Invalid Write width."); break; } } UInt32 AddLabelRef(Token tok, int writeWidth, bool absolute, out FeedMe feedMe) { feedMe = new FeedMe { token = tok, bytePos = writer.BaseStream.Position, refType = RefType.Label, subType = (absolute ? RefSubType.Absolute : RefSubType.Relative), width = (byte)writeWidth, offset = address, }; feedMes.Add(feedMe); return((UInt32)Placeholders.Label); } void AddVarRef(Token tok, int writeWidth) { feedMes.Add(new FeedMe { token = tok, bytePos = writer.BaseStream.Position, refType = RefType.Const, width = (byte)writeWidth, }); UInt32 ii = (UInt32)Placeholders.Variable; Write(ii, writeWidth); } void ConstCreate(Token tok, int val) { consts.Add(new Const { token = tok, val = val, }); } void WriteData() { foreach (var strData in stringData) { strData.bytePos = writer.BaseStream.Position + 1; // @TODO C# length-prefixed strings? writer.Write(strData.str); writer.Write((byte)0); } isDataWritten = true; } try { for (; iT < tokens.Count; iT++) { string token = tokens[iT].token; Token tok = tokens[iT]; Instructions instruction; if (token[0] == '.') { RequireToken(":"); if (writer == null) { writer = new BinaryWriter(File.Open(outputFilePath, FileMode.Create), Encoding.Default); } labels.Add(new Label { bytePos = writer.BaseStream.Position, symbol = token }); continue; } else if (token[0] == '#') { int put; switch (token) { case "#bits": UInt32 bitNum = RequireIntegerLiteral(); switch (bitNum) { case 16: bits = Bits.Bits16; break; case 32: bits = Bits.Bits32; break; default: ThrowError("Invalid #bits value."); break; } continue; case "#extension": string ext = RequireStringLiteral(); if (writer == null) { outputFilePath = Path.ChangeExtension(filePath, ext); } else { ThrowError("Can't change the extension after file writing has started."); } continue; case "#format": { string next = GetNext(); if (Enum.TryParse(next, out format) == false) { ThrowError("Expected format, got " + next); } continue; } case "#address": { address = RequireIntegerLiteral(); continue; } case "#Align": { uint alignBy = RequireIntegerLiteral(); long left = (writer.BaseStream.Position) % alignBy; if (left != 0) { left = alignBy - left; for (int j = 0; j < left; j++) { writer.Write((byte)0x90); } } continue; } case "#Put1": put = 1; goto PutSub; case "#Put2": put = 2; goto PutSub; case "#Put4": put = 4; goto PutSub; case "#Put": put = ConstMathParse(); RequireToken(":"); PutSub: { int wasPut = put; if (writer == null) { writer = new BinaryWriter(File.Open(outputFilePath, FileMode.Create), Encoding.Default); } Token next = GetNextToken(); if (TryStringLit(next, out string str)) // string lit { /*if (str.Length > put) * ThrowError("“" + str + "” is longer than " + put);*/ for (int j = 0; j < str.Length; j++) { writer.Write((byte)str[j]); put--; } for (int j = 0; j < put; j++) { writer.Write((byte)0); } } else if (TryLabel(next)) // label { Write(AddLabelRef(next, put, true, out FeedMe feedMe), put); } else if (TryIntLiteralParse(next.token, out UInt32 ii)) // int lit { if (put > 4) { for (int j = 0; j < put; j++) { writer.Write((byte)ii); } } else { Write(ii, put); } } else // var { AddVarRef(next, put); } if (PeekNext() == ",") { GetNext(); put = wasPut; goto PutSub; } continue; } case "#PutData": case "#WriteData": WriteData(); continue; default: ThrowError("Invalid compiler directive."); break; } } if (PeekNext() == "=") { Token varTok = tok; RequireToken("="); int val = ConstMathParse(); ConstCreate(tok, val); continue; } if (Enum.TryParse(token, true, out instruction) == false || Enum.IsDefined(typeof(Instructions), instruction) == false) // @TODO integers get parsed as enum { return(ThrowError("Invalid instruction.")); } if (writer == null) { writer = new BinaryWriter(File.Open(outputFilePath, FileMode.Create), Encoding.Default); } byte forceWidth = 0; switch (instruction) { case Instructions.Ret: if (TryIntLiteralParse(PeekNext(), out UInt32 ii)) { GetNextToken(); writer.Write((byte)0xC2); Write(ii, 2); } else { writer.Write((byte)0xc3); } break; case Instructions.Cli: writer.Write((byte)0xfa); break; case Instructions.Hlt: writer.Write((byte)0xf4); break; case Instructions.LodsB: writer.Write((byte)0xAC); break; case Instructions.Inc: writer.Write((byte)(0x40 + RegisterNumber(RequireRegister(out forceWidth)))); break; case Instructions.Pop: writer.Write((byte)(0x58 + RegisterNumber(RequireRegister(out forceWidth)))); break; case Instructions.Int: writer.Write((byte)0xcd); Write(RequireIntegerLiteral(), 1); break; case Instructions.MovB: forceWidth = 1; // @TODO enforce forceWidth goto MovCommon; case Instructions.Mov: MovCommon: { Side dest = RequireSide(0); RequireEitherToken("=", ","); Side src = RequireSide(dest.width); byte opcode; if (dest.reg != Registers.None && src.reg == Registers.None && src.isMemoryAccess == false && dest.isMemoryAccess == false) { switch (dest.width) { case 1: opcode = 0xb0; // MovRImmB break; case 2: if (bits == Bits.Bits32) { ThrowError("Compiler error: 16-bit Mov in 32-bit code not implemented. Sorry!"); // @TODO 16bit vs 32bit } opcode = 0xb8; // MovRImmW break; case 4: opcode = 0xb8; // MovRImmL break; default: ThrowError($"Compiler error: Invalid register width: “{dest}” of width {dest.width}."); break; } writer.Write((byte)(opcode + RegisterNumber(dest.reg))); if (src.feedMe != null) { src.feedMe.bytePos++; } Write(src.offset, dest.width); } else if (dest.isMemoryAccess && src.isMemoryAccess) { ThrowError("Invalid instruction – cannot access memory on both sides."); } else if (src.isMemoryAccess) { // @TODO @cleanup dupl byte modRegRM; byte? write1 = null; UInt32?write4 = null; if (src.reg == Registers.None) { modRegRM = (byte)ModRegRM.RMemImm; write4 = src.offset; } else if (src.offset != 0 || src.reg == Registers.Ebp) { if (src.offset > -127 && src.offset < 128) { modRegRM = (byte)ModRegRM.ROffset1RMem; write1 = (byte)src.offset; } else { modRegRM = (byte)ModRegRM.ROffset4RMem; write4 = src.offset; } modRegRM |= (byte)(RegisterNumber(src.reg)); } else { modRegRM = (byte)ModRegRM.RRMem; modRegRM |= (byte)(RegisterNumber(src.reg)); } modRegRM |= (byte)(RegisterNumber(dest.reg) << 3); switch (dest.width) { case 1: opcode = 0x8A; break; case 2: if (bits == Bits.Bits32) { ThrowError("Compiler error: 16-bit Mov in 32-bit code not implemented. Sorry!"); // @TODO 16bit vs 32bit } opcode = 0x8B; break; case 4: opcode = 0x8B; break; default: ThrowError($"Compiler error: Invalid register width: “{dest.reg}” of width {dest.width}."); break; } writer.Write(opcode); writer.Write(modRegRM); if (src.feedMe != null) { src.feedMe.bytePos += 2; } if (write1 != null) { writer.Write((byte)write1); } if (write4 != null) { writer.Write((UInt32)write4); } } else if (dest.isMemoryAccess && src.reg == Registers.None) { byte modRegRM = (byte)((byte)ModRegRM.RRMem | RegisterNumber(dest.reg)); if (src.offset < 256) { writer.Write((byte)0xC6); writer.Write(modRegRM); writer.Write((byte)src.offset); } else { writer.Write((byte)0xC7); writer.Write(modRegRM); writer.Write((UInt32)src.offset); } } else if (dest.isMemoryAccess && src.reg != Registers.None) { // @TODO @cleanup dupl byte modRegRM; byte? write1 = null; UInt32?write4 = null; if (dest.reg == Registers.None) { modRegRM = (byte)ModRegRM.RMemImm; write4 = dest.offset; } else if (dest.offset != 0 || dest.reg == Registers.Ebp) { if (dest.offset > -127 && dest.offset < 128) { modRegRM = (byte)ModRegRM.ROffset1RMem; write1 = (byte)dest.offset; } else { modRegRM = (byte)ModRegRM.ROffset4RMem; write4 = dest.offset; } modRegRM |= (byte)(RegisterNumber(dest.reg)); } else { modRegRM = (byte)ModRegRM.RRMem; modRegRM |= (byte)(RegisterNumber(dest.reg)); } modRegRM |= (byte)(RegisterNumber(src.reg) << 3); switch (src.width) { case 1: opcode = 0x88; break; case 2: if (bits == Bits.Bits32) { ThrowError("Compiler error: 16-bit Mov in 32-bit code not implemented. Sorry!"); // @TODO 16bit vs 32bit } opcode = 0x89; break; case 4: opcode = 0x89; break; default: ThrowError($"Compiler error: Invalid register width: “{src.reg}” of width {src.width}."); break; } writer.Write(opcode); writer.Write(modRegRM); if (dest.feedMe != null) { dest.feedMe.bytePos += 2; } if (write1 != null) { writer.Write((byte)write1); } if (write4 != null) { writer.Write((UInt32)write4); } } else if (dest.reg != Registers.None && src.reg != Registers.None) { byte modRegRM = (byte)((byte)ModRegRM.RToR | RegisterNumber(dest.reg) | (RegisterNumber(src.reg) << 3)); switch (dest.width) { case 1: opcode = 0x88; break; case 2: if (bits == Bits.Bits32) { ThrowError("Compiler error: 16-bit Mov in 32-bit code not implemented. Sorry!"); // @TODO 16bit vs 32bit } opcode = 0x89; break; case 4: opcode = 0x89; break; default: ThrowError($"Compiler error: Invalid register width: “{dest}” of width {dest.width}."); break; } writer.Write(opcode); writer.Write(modRegRM); } else { ThrowError("Compiler error: invalid instruction."); } break; } case Instructions.PushL: forceWidth = 4; // @TODO enforce forceWidth goto PushCommon; case Instructions.Push: PushCommon: { Side side = RequireSide(0); if (side.isMemoryAccess) { ThrowError("Not implemented. Sorry!"); } if (side.reg != Registers.None) { writer.Write((byte)(0x50 + RegisterNumber(side.reg))); } else { if (forceWidth == 0) { if (bits == Bits.Bits16) { forceWidth = 2; } else { forceWidth = 4; } } writer.Write((byte)0x68); if (side.feedMe != null) { side.feedMe.bytePos += 1; side.feedMe.width = forceWidth; } Write(side.offset, forceWidth); } //Write(RequireLiteral(forceWidth), forceWidth); break; } case Instructions.CallL: { // FF /2 writer.Write((byte)0xFF); writer.Write((byte)((byte)ModRegRM.RMemImm | 0b00_010_000)); RequireToken("["); Write(RequireLiteral(4), 4); RequireToken("]"); break; } case Instructions.CmpB: { byte modRegRM = (byte)ModRegRM.RToR | (7 << 3); Registers dest = RequireRegister(out byte width); modRegRM |= (byte)RegisterNumber(dest); if (width != 1) { ThrowError("Non-matching register width."); } RequireToken(","); writer.Write((byte)0x80); writer.Write((byte)modRegRM); Write(RequireLiteral(width), width); break; } case Instructions.Je: writer.Write((byte)0x74); goto JmpCommon; case Instructions.Jne: writer.Write((byte)0x75); goto JmpCommon; case Instructions.Jmp: writer.Write((byte)0xEB); JmpCommon: { Write(RequireLabel(1, false), 1); } break; case Instructions.Call: { byte width; if (bits == Bits.Bits16) { width = 2; } else { width = 4; } writer.Write((byte)0xE8); Write(RequireLabel(width, false), width); } break; default: return(ThrowError("Compiler error: Instruction not implemented. Sorry!")); } } if (isDataWritten == false) { WriteData(); } foreach (var feedMe in feedMes) { switch (feedMe.refType) { case RefType.Label: { Label found = labels.Find(x => x.symbol == feedMe.token.token); if (found == null) { ThrowError("Undeclared label: “" + feedMe.token.token + "”", feedMe.token); } writer.Seek((int)feedMe.bytePos, SeekOrigin.Begin); // @WTF Seek wants int but Position is long?? if (feedMe.subType == RefSubType.Absolute) { Write((UInt32)found.bytePos + feedMe.offset, feedMe.width); } else { Write((uint)(found.bytePos - (feedMe.bytePos + feedMe.width)), feedMe.width); } break; } case RefType.String: writer.Seek((int)feedMe.bytePos, SeekOrigin.Begin); Write((UInt32)(feedMe.refObj.bytePos + feedMe.offset), feedMe.width); break; case RefType.Const: { Const found = consts.Find(x => x.token.token == feedMe.token.token); if (found == null) { ThrowError("Undeclared constant: “" + feedMe.token.token + "”", feedMe.token); } writer.Seek((int)feedMe.bytePos, SeekOrigin.Begin); Write((UInt32)(found.val), feedMe.width); break; } default: ThrowError("Compiler error: unknown feedMe.refType."); break; } } } catch (CompilerException ex) { return(AddError(ex.Message, ex.token)); } finally { if (writer != null) { writer.Dispose(); } } return(true); }
int ConstMathParse(Compilers.Token next = null) { bool canEnd = false; MathEl curEl = new MathEl(); Stack <MathEl> stack = new Stack <MathEl>(); stack.Push(curEl); uint DoOp(uint left, char op, uint right) { switch (op) { case '+': left += right; break; case '-': left -= right; break; case '*': left *= right; break; case '/': left /= right; break; } return(left); } bool DoVal(uint val) { if (curEl.op != 0 && curEl.val != null) { curEl.val = DoOp((uint)curEl.val, curEl.op, val); } else if (curEl.val == null) { curEl.val = val; } else { return(this.ThrowError("Error: This shouldn't happen.")); } if (stack.Count == 1) { canEnd = true; } return(true); } int End() { iT--; return((int)curEl.val); } int ThrowError(string message) { this.ThrowError(message); return(-1); } if (next == null) { iT++; } int iMax = tokens.Count; while (iT < iMax) { uint newVal; switch (tokens[iT].token) { case "+": case "-": case "*": case "/": curEl.op = tokens[iT].token[0]; canEnd = false; break; case ":": case ";": if (canEnd) { return(End()); } else { return(ThrowError("Unexpected: " + tokens[iT].token)); } case "(": if ((curEl.op == 0 && curEl.val == null) || curEl.val != null) { curEl = new MathEl(); stack.Push(curEl); canEnd = false; } else { return(ThrowError("Unexpected '('.")); } break; case ")": if (stack.Count > 1) { stack.Pop(); MathEl prevEl = stack.Peek(); if (prevEl.op != 0) { prevEl.val = DoOp((uint)prevEl.val, prevEl.op, (uint)curEl.val); } else { prevEl.val = curEl.val; } curEl = prevEl; if (stack.Count == 1) { canEnd = true; } } else if (canEnd) { return((int)curEl.val); } else { return(ThrowError("Unexpected ')'.")); } break; default: if (canEnd) { return(End()); } else if (tokens[iT].token == "$") { if (DoVal((uint)writer.BaseStream.Position) == false) { return(-1); } } else if (TryIntLiteralParse(tokens[iT].token, out newVal)) { if (DoVal(newVal) == false) { return(-1); } } else if (tokens[iT].token[0] == '.') { Label found = labels.Find(x => x.symbol == tokens[iT].token); if (found == null) { ThrowError("Undeclared label: “" + tokens[iT].token + "”"); } if (DoVal((uint)found.bytePos) == false) { return(-1); } } else { Const found = consts.Find(x => x.token.token == tokens[iT].token); if (found == null) { ThrowError("Can't parse this literal, or undefined constant: " + tokens[iT].token); } if (DoVal((uint)found.val) == false) { return(-1); } canEnd = true; //ThrowError("Unexpected: " + tokens[iT].token); } break; } iT++; } if (canEnd) { return(End()); } else { return(ThrowError("Unexpected end of file.")); } }