private Listing HandleDirective(string line, bool passTwo = false) { string directive = line.Substring(1).Trim(); string[] parameters = new string[0]; string parameter = ""; if (directive.SafeContains(' ')) { parameter = directive.Substring(directive.SafeIndexOf(' ') + 1); parameters = parameter.SafeSplit(' '); directive = directive.Remove(directive.SafeIndexOf(' ')); } directive = directive.ToLower(); var listing = new Listing { Code = line, CodeType = CodeType.Directive, Address = PC, Error = AssemblyError.None, Warning = AssemblyWarning.None, FileName = FileNames.Peek(), LineNumber = LineNumbers.Peek(), RootLineNumber = RootLineNumber }; try { switch (directive) { case "block": { ulong amount = ExpressionEngine.Evaluate(parameter, PC, RootLineNumber); listing.Output = new byte[amount]; PC += (uint)amount; return listing; } case "byte": case "db": { if (passTwo) { var result = new List<byte>(); parameters = parameter.SafeSplit(','); foreach (var p in parameters) { if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\"")) result.AddRange( Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Trim().Length - 2).Unescape())); else { try { result.Add((byte)ExpressionEngine.Evaluate(p, PC++, RootLineNumber)); } catch (KeyNotFoundException) { listing.Error = AssemblyError.UnknownSymbol; } } } listing.Output = result.ToArray(); return listing; } else { parameters = parameter.SafeSplit(','); int length = 0; foreach (var p in parameters) { if (p.StartsWith("\"") && p.EndsWith("\"")) length += p.Substring(1, p.Length - 2).Unescape().Length; else length++; } listing.Output = new byte[length]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } } case "word": case "dw": { if (passTwo) { var result = new List<byte>(); parameters = parameter.SafeSplit(','); foreach (var item in parameters) result.AddRange(TruncateWord(ExpressionEngine.Evaluate(item, PC++, RootLineNumber))); listing.Output = result.ToArray(); return listing; } else { listing.Output = new byte[parameters.Length * (InstructionSet.WordSize / 8)]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } } case "error": case "echo": { if (passTwo) { string output = ""; bool formatOutput = false; List<object> formatParameters = new List<object>(); foreach (var item in parameters) { if (item.Trim().StartsWith("\"") && item.EndsWith("\"")) { output += item.Substring(1, item.Length - 2); formatOutput = true; } else { if (!formatOutput) output += ExpressionEngine.Evaluate(item, PC, RootLineNumber); else { formatParameters.Add(ExpressionEngine.Evaluate(item, PC, RootLineNumber)); } } } if (formatOutput) output = string.Format(output, formatParameters.ToArray()); Console.WriteLine((directive == "error" ? "User Error: " : "") + output); return listing; } else { listing.PostponeEvalulation = true; return listing; } } break; case "end": return listing; case "fill": { parameters = parameter.SafeSplit(','); ulong amount = ExpressionEngine.Evaluate(parameters[0], PC, RootLineNumber); if (parameters.Length == 1) { Array.Resize<string>(ref parameters, 2); parameters[1] = "0"; } listing.Output = new byte[amount]; for (int i = 0; i < (int)amount; i++) listing.Output[i] = (byte)ExpressionEngine.Evaluate(parameters[1], PC++, RootLineNumber); return listing; } case "org": PC = (uint)ExpressionEngine.Evaluate(parameter, PC, RootLineNumber); return listing; case "include": { string file = GetIncludeFile(parameter); if (file == null) { listing.Error = AssemblyError.FileNotFound; return listing; } FileNames.Push(Path.GetFileName(parameter.Substring(1, parameter.Length - 2))); LineNumbers.Push(0); string includedFile = File.ReadAllText(file) + "\n.endfile"; string[] lines = includedFile.Replace("\r", "").Split('\n'); Lines = Lines.Take(CurrentIndex + 1) .Concat(lines) .Concat(Lines.Skip(CurrentIndex + 1)) .ToArray(); return listing; } case "endfile": // Special, undocumented directive RootLineNumber--; LineNumbers.Pop(); FileNames.Pop(); return null; case "equ": if (parameters.Length == 1) { if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower())) { listing.Error = AssemblyError.DuplicateName; return listing; } ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol(1)); } else { if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower())) { listing.Error = AssemblyError.DuplicateName; return listing; } ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol( (uint) ExpressionEngine.Evaluate( parameter.Substring( parameter.IndexOf(' ') + 1) .Trim(), PC, RootLineNumber))); } return listing; case "exec": if (parameters.Length == 0 || !AllowExec) { listing.Error = AssemblyError.InvalidDirective; return listing; } else { var process = new ProcessStartInfo(parameters[0], string.Join(" ", parameters.Skip(1).ToArray())); process.UseShellExecute = false; process.RedirectStandardOutput = true; var p = Process.Start(process); var output = p.StandardOutput.ReadToEnd().Trim('\n', '\r', ' '); p.WaitForExit(); listing.Output = Settings.Encoding.GetBytes(output); PC += (uint)listing.Output.Length; return listing; } case "define": if (parameters.Length == 1) { if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower())) { listing.Error = AssemblyError.DuplicateName; return listing; } ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol(1)); } else { var macro = new Macro(); if (parameter.Contains("(")) { var parameterDefinition = parameter.Substring(parameter.SafeIndexOf('(') + 1); parameterDefinition = parameterDefinition.Remove(parameterDefinition.SafeIndexOf(')')); // NOTE: This probably introduces the ability to use ".macro foo(bar)this_doesnt_cause_errors" macro.Parameters = parameterDefinition.SafeSplit(','); for (int i = 0; i < macro.Parameters.Length; i++) macro.Parameters[i] = macro.Parameters[i].Trim(); macro.Name = parameter.Remove(parameter.SafeIndexOf('(')); macro.Code = parameter.Substring(parameter.SafeIndexOf(')') + 1); } else { macro.Name = parameter.Remove(parameter.SafeIndexOf(' ') + 1); // TODO: Consider enforcing character usage restrictions macro.Code = parameter.Substring(parameter.SafeIndexOf(' ') + 1).Trim(); } macro.Name = macro.Name.ToLower().Trim(); if (Macros.Any(m => m.Name == macro.Name && m.Parameters.Length == macro.Parameters.Length)) { listing.Error = AssemblyError.DuplicateName; return listing; } Macros.Add(macro); } return listing; case "if": if (parameters.Length == 0) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (!IfStack.Peek()) { WorkingIfStack.Push(false); return listing; } try { IfStack.Push(ExpressionEngine.Evaluate(parameter, PC, RootLineNumber) != 0); } catch (InvalidOperationException) { listing.Error = AssemblyError.InvalidExpression; } catch (KeyNotFoundException) { listing.Error = AssemblyError.UnknownSymbol; } return listing; case "ifdef": { if (parameters.Length != 1) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (!IfStack.Peek()) { WorkingIfStack.Push(false); return listing; } var result = ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower()); if (!result) result = Macros.Any(m => m.Name.ToLower() == parameters[0].ToLower()); IfStack.Push(result); return listing; } case "ifndef": { if (parameters.Length != 1) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (!IfStack.Peek()) { WorkingIfStack.Push(false); return listing; } var result = ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower()); if (!result) result = Macros.Any(m => m.Name.ToLower() == parameters[0].ToLower()); IfStack.Push(!result); return listing; } case "endif": if (parameters.Length != 0) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (IfStack.Count == 1) { listing.Error = AssemblyError.UncoupledStatement; return listing; } if (WorkingIfStack.Any()) { WorkingIfStack.Pop(); return listing; } IfStack.Pop(); return listing; case "else": if (parameters.Length != 0) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (WorkingIfStack.Any()) return listing; IfStack.Push(!IfStack.Pop()); return listing; //case "elif": // TODO: Requires major logic changes //case "elseif": // if (IfStack.Peek()) // { // IfStack.Pop(); // IfStack.Push(false); // return listing; // } // return listing; case "ascii": if (parameters.Length == 0) { listing.Error = AssemblyError.InvalidDirective; return listing; } if (!(parameter.StartsWith("\"") && parameter.EndsWith("\""))) { listing.Error = AssemblyError.InvalidDirective; return listing; } parameter = parameter.Substring(1, parameter.Length - 2); listing.Output = Settings.Encoding.GetBytes(parameter.Unescape()); return listing; case "asciiz": if (passTwo) { var result = new List<byte>(); parameters = parameter.SafeSplit(','); foreach (var p in parameters) { if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\"")) result.AddRange(Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Length - 2).Unescape())); else listing.Error = AssemblyError.InvalidDirective; } listing.Output = new byte[result.Count + 1]; Array.Copy(result.ToArray(), listing.Output, result.Count); listing.Output[listing.Output.Length - 1] = 0; return listing; } else { parameters = parameter.SafeSplit(','); int length = 0; foreach (var p in parameters) { if (p.StartsWith("\"") && p.EndsWith("\"")) length += p.Substring(1, p.Length - 2).Unescape().Length; else listing.Error = AssemblyError.InvalidDirective; } length++; listing.Output = new byte[length]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } break; case "asciip": if (passTwo) { var result = new List<byte>(); parameters = parameter.SafeSplit(','); foreach (var p in parameters) { if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\"")) result.AddRange(Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Length - 2).Unescape())); else listing.Error = AssemblyError.InvalidDirective; } listing.Output = new byte[result.Count + 1]; listing.Output[0] = (byte)result.Count; Array.Copy(result.ToArray(), 0, listing.Output, 1, result.Count); return listing; } else { parameters = parameter.SafeSplit(','); int length = 0; foreach (var p in parameters) { if (p.StartsWith("\"") && p.EndsWith("\"")) length += p.Substring(1, p.Length - 2).Unescape().Length; else listing.Error = AssemblyError.InvalidDirective; } length++; listing.Output = new byte[length]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } break; case "nolist": Listing = false; return listing; case "list": Listing = true; return listing; case "undefine": if (parameters.Length == 0) { listing.Error = AssemblyError.InvalidDirective; return listing; } foreach (var item in parameters) { if (Macros.Any(m => m.Name == item.ToLower())) Macros.Remove(Macros.FirstOrDefault(m => m.Name == item)); else if (ExpressionEngine.Symbols.ContainsKey(item.ToLower())) ExpressionEngine.Symbols.Remove(item.ToLower()); else { listing.Error = AssemblyError.UnknownSymbol; return listing; } } return listing; } } catch (KeyNotFoundException) { listing.Error = AssemblyError.UnknownSymbol; return listing; } catch (InvalidOperationException) { listing.Error = AssemblyError.InvalidExpression; return listing; } return null; }
private Listing HandleDirective(string line, bool passTwo = false) { string directive = line.Substring(1).Trim(); string[] parameters = new string[0]; string parameter = ""; if (directive.SafeContains(' ')) { parameter = directive.Substring(directive.SafeIndexOf(' ') + 1); parameters = parameter.SafeSplit(','); directive = directive.Remove(directive.SafeIndexOf(' ')); } var listing = new Listing { Code = line, CodeType = CodeType.Directive, Address = PC, Error = AssemblyError.None, Warning = AssemblyWarning.None, FileName = FileNames.Peek(), LineNumber = LineNumbers.Peek(), RootLineNumber = RootLineNumber }; switch (directive) { case "block": { ulong amount = ExpressionEngine.Evaluate(parameter, PC); listing.Output = new byte[amount]; PC += (uint)amount; return listing; } case "byte": case "db": { if (passTwo) { var result = new List<byte>(); foreach (var item in parameters) result.Add((byte)ExpressionEngine.Evaluate(item, PC++)); listing.Output = result.ToArray(); return listing; } else { listing.Output = new byte[parameters.Length]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } } case "word": case "dw": { if (passTwo) { var result = new List<byte>(); foreach (var item in parameters) result.AddRange(TruncateWord(ExpressionEngine.Evaluate(item, PC++))); listing.Output = result.ToArray(); return listing; } else { listing.Output = new byte[parameters.Length * (InstructionSet.WordSize / 8)]; listing.PostponeEvalulation = true; PC += (uint)listing.Output.Length; return listing; } } case "error": case "echo": { string output = ""; foreach (var item in parameters) { if (item.Trim().StartsWith("\"") && item.EndsWith("\"")) output += item.Substring(1, item.Length - 2); else output += ExpressionEngine.Evaluate(item, PC); } Console.WriteLine((directive == "error" ? "User Error: " : "") + output); return listing; } case "nolist": case "list": // TODO: Do either of these really matter? case "end": return listing; case "fill": { ulong amount = ExpressionEngine.Evaluate(parameters[0], PC); if (parameters.Length == 1) { Array.Resize<string>(ref parameters, 2); parameters[1] = "0"; } listing.Output = new byte[amount]; for (int i = 0; i < (int)amount; i++) listing.Output[i] = (byte)ExpressionEngine.Evaluate(parameters[1], PC++); return listing; } case "option": // TODO: Spasm-style bitmap imports return listing; case "org": PC = (uint)ExpressionEngine.Evaluate(parameter, PC); return listing; case "include": { string file = GetIncludeFile(parameter); if (file == null) { listing.Error = AssemblyError.FileNotFound; return listing; } FileNames.Push(parameter); LineNumbers.Push(0); string includedFile = File.ReadAllText(file) + "\n.endfile"; string[] lines = includedFile.Replace("\r", "").Split('\n'); Lines = Lines.Take(CurrentIndex + 1).Concat(lines).Concat(Lines.Skip(CurrentIndex + 1)).ToArray(); return listing; } case "endfile": // Special directive RootLineNumber--; LineNumbers.Pop(); FileNames.Pop(); return null; case "equ": case "define": // TODO: Macro // TODO: Equates in a different way ExpressionEngine.Equates.Add(parameters[0], (uint)ExpressionEngine.Evaluate(parameter.Substring(parameter.IndexOf(',') + 1).Trim(), PC)); return listing; } return null; }