private void AssembleCode() { //resolve external dependencies try { foreach (string s in imports) { CodeInformation.SymbolInformation si = new CodeInformation.SymbolInformation { LibraryName = s + ".dll", Functions = new List <CodeInformation.FunctionInformation>() }; //import values string[] lines = null; string temp = "libraries/" + s + ".export"; //.exe is in root directory if (File.Exists(temp)) { lines = File.ReadAllLines(temp); } //.exe is in a /bin/ directory else if (File.Exists(temp = "../" + temp)) { lines = File.ReadAllLines(temp); } //.exe is in Visual Studio's /bin/debug or /bin/release else if (File.Exists(temp = "../" + temp)) { lines = File.ReadAllLines(temp); } else { throw new FileNotFoundException("Fatal Error: Cannot find file '" + s + ".export'"); } foreach (var l in lines) { //ideally this should be replaced with regex @"(\d)* *(\w)+" //hard coding this stuff isn't the best idea. var split = l.Split(' '); if (unresolvedReferences.ContainsKey(split[split.Length - 1])) { var func = new CodeInformation.FunctionInformation { FunctionName = split[split.Length - 1], Ordinal = int.Parse(split[0]), Replacements = new List <int>() }; unresolvedReferences[split[split.Length - 1]] = func; si.Functions.Add(func); } } if (si.Functions.Count > 0) { ci.SymbolInfo.Add(si); } } } catch (FileNotFoundException) { throw; } catch (Exception ex) { //should never happen if installed correctly throw new Exception(FormatError("Unable to read import files: " + ex.Message)); } bool found_main = false; //first calculate byte offsets for everything offset = 0; foreach (UnprocessedInstruction instruction in unprocessed_code) { if (instruction.isLabel) { if (instruction.ident.Equals("main")) { ci.EntryPoint = (uint)offset; found_main = true; } labels.Add(instruction.ident, offset); } else { foreach (Instruction instruct in x86InstructionSet.x86Instructions) { if (instruction.ident.Equals(instruct.mnemonic)) { instruction.instruction = instruct; break; } } if (instruction.instruction == null) { throw new Exception(FormatError("Invalid opcode.", instruction.main_token.lineNum, instruction.main_token.linePos)); } if (!isValidArgument(instruction.arg0, instruction.instruction.arg1) || !isValidArgument(instruction.arg1, instruction.instruction.arg2) || !isValidArgument(instruction.arg2, instruction.instruction.arg3)) { throw new Exception(FormatError("Invalid operand", instruction.main_token.lineNum, instruction.main_token.linePos)); } //I swear officer, this hack isn't mine. Please don't arrest me. //need to replace for external calls if (instruction.ident.Equals("call")) { if (instruction.arg0.ident != null) { if (unresolvedReferences[instruction.arg0.ident] != null) { //external code instruction.arg0.offsetValue = 1; unresolvedReferences[instruction.arg0.ident].Replacements.Add(offset + 2); } } } else if (instruction.ident.Equals("mov")) { if (instruction.arg1.ident != null) { //can be either a string, label, or external function if (unresolvedReferences[instruction.arg1.ident] != null) { instruction.arg1.offsetValue = 1; //external function unresolvedReferences[instruction.arg1.ident].Replacements.Add(offset + 2); } else if (stringTable.ContainsKey(instruction.arg1.ident)) { instruction.arg1.offsetValue = 1; //string table stringTable[instruction.arg1.ident].Item2.Add(offset + 2); } //labels handled elsewhere } } //replace string table calls else if (instruction.ident.Equals("push")) { if (instruction.arg0.ident != null) { if (stringTable.ContainsKey(instruction.arg0.ident)) { //string table instruction.arg0.offsetValue = 1; stringTable[instruction.arg0.ident].Item2.Add(offset + 1); } } } instruction.offset = offset; offset += instruction.instruction.numberOfBytes(instruction.arg0, instruction.arg1, instruction.arg2); } } if (!found_main) { throw new Exception(FormatError("Could not find entrypoint. Expected 'main' label.")); } //remove all resolved external references/string references/label references foreach (var key in unresolvedReferences.Keys.ToArray()) { if (unresolvedReferences[key] != null) { unresolvedReferences.Remove(key); } else if (stringTable.ContainsKey(key)) { unresolvedReferences.Remove(key); } else if (labels.ContainsKey(key)) { unresolvedReferences.Remove(key); } } if (unresolvedReferences.Count > 0) { foreach (var v in unresolvedReferences) { warnings.Add(FormatWarning("Unresolved reference to '" + v.Key + "'")); } throw new Exception(FormatError("Cannot continue compilation.")); } //"The magnitude of this hack compares favorably with that of the national debt." - A Microsoft Programmer //resolve references for internal calls and jumps foreach (var unpr in unprocessed_code) { if (unpr.ident.Equals("push") && unpr.arg0.type == InstructionArg.LABEL) { if (unpr.arg0.offsetValue != 1) { if (labels.ContainsKey(unpr.arg0.ident)) { unpr.arg0.value = labels[unpr.arg0.ident] - (unpr.offset + unpr.instruction.numberOfBytes(unpr.arg0, unpr.arg1, unpr.arg2)); unpr.arg0.type = InstructionArg.IMM32; } else { throw new Exception(FormatError("Unresolved reference '" + unpr.arg0.ident + "'", unpr.main_token.lineNum, unpr.main_token.linePos)); } } } else if (unpr.ident.Equals("mov")) { //if not external if (unpr.arg1.ident != null && unpr.arg1.offsetValue != 1) { if (labels.ContainsKey(unpr.arg1.ident)) { unpr.arg1.value = labels[unpr.arg1.ident] - (unpr.offset + unpr.instruction.numberOfBytes(unpr.arg0, unpr.arg1, unpr.arg2)); } else { throw new Exception(FormatError("Unresolved reference '" + unpr.arg1.ident + "'", unpr.main_token.lineNum, unpr.main_token.linePos)); } } } else if (unpr.ident.Equals("call") || unpr.ident.Equals("jmp") || unpr.ident.Equals("jg") || unpr.ident.Equals("je") || unpr.ident.Equals("jge") || unpr.ident.Equals("jl") || unpr.ident.Equals("jle") || unpr.ident.Equals("jne") || unpr.ident.Equals("loop") || unpr.ident.Equals("loope") || unpr.ident.Equals("loopne")) { //if not external if (unpr.arg0.offsetValue != 1) { if (labels.ContainsKey(unpr.arg0.ident)) { unpr.arg0.value = labels[unpr.arg0.ident] - (unpr.offset + unpr.instruction.numberOfBytes(unpr.arg0, unpr.arg1, unpr.arg2)); } else { throw new Exception(FormatError("Unresolved reference '" + unpr.arg0.ident + "'", unpr.main_token.lineNum, unpr.main_token.linePos)); } } } } UnprocessedInstruction last_inst = null; try { //assemble everything foreach (var unpr in unprocessed_code) { last_inst = unpr; if (!unpr.isLabel) { code.AddRange(unpr.instruction.assemble(unpr.arg0, unpr.arg1, unpr.arg2)); } } } catch (Exception ex) { throw new Exception( FormatError(ex.Message, last_inst.main_token.lineNum, last_inst.main_token.linePos), ex); } }
private void ParseCode() { //Expect { instruction } [string] (arg1 (, arg2 (, arg3 ))) //Expect { label } [string]: for (; index + 1 < tokens.Count && !tokens[index + 1].Equals(".");) { Expect(typeof(string), "Expected a label or instruction"); if (index + 1 < tokens.Count && tokens[index + 1].Equals(":")) { unprocessed_code.Add(new UnprocessedInstruction { ident = tokens[index], isLabel = true, main_token = tokens[index] }); index += 2; //a label does not need to be on its own line } UnprocessedInstruction inst = new UnprocessedInstruction { ident = tokens[index], isLabel = false, main_token = tokens[index] }; if (index < tokens.Count && !nextTokenIsNextLine()) { index++; inst.arg0 = ParseArg(); if (index < tokens.Count && !nextTokenIsNextLine()) { index++; if (!tokens[index].Equals(",")) { throw new Exception(FormatError("Expected a delimiter between arguments.", tokens[index].lineNum, tokens[index].linePos)); } index++; inst.arg1 = ParseArg(); if (index < tokens.Count && !nextTokenIsNextLine()) { index++; if (!tokens[index].Equals(",")) { throw new Exception(FormatError("Expected a delimiter between arguments.", tokens[index].lineNum, tokens[index].linePos)); } index++; inst.arg2 = ParseArg(); if (!nextTokenIsNextLine()) { throw new Exception( FormatError("Unexpected arg count. No instruction has more than 3 arguments.", tokens[index].lineNum, tokens[index].linePos)); } } } } unprocessed_code.Add(inst); } }