Skip to content

Commit

Permalink
Add debug info
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperJMN committed Nov 4, 2022
1 parent 3852e32 commit bb49876
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 125 deletions.
16 changes: 16 additions & 0 deletions Sixty502DotNet.Tests/Z80AssemblerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Sixty502DotNet.Tests
{
[TestClass]
public class Z80AssemblerTests
{
[TestMethod]
public void Test()
{
var sut = new Z80Assembler();
var input = ";\r\n;\r\nCALL MAIN \r\n HALT \r\nMAIN: \r\n LD a,1 \r\n LD b,2 \r\n ADD a,b \r\n RET \r\n";
var bytes = sut.Assemble(input);
}
}
}
16 changes: 16 additions & 0 deletions Sixty502DotNet/src/AssemblyData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using Sixty502DotNet.Runtime;

namespace Sixty502DotNet;

public class AssemblyData
{
public byte[] ProgramBinary { get; }
public IEnumerable<DebugEntry> DebugInfo { get; }

public AssemblyData(byte[] programBinary, IEnumerable<DebugEntry> debugInfo)
{
ProgramBinary = programBinary;
DebugInfo = debugInfo;
}
}
60 changes: 37 additions & 23 deletions Sixty502DotNet/src/CodeGen/BlockVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
//
//-----------------------------------------------------------------------------

using Antlr4.Runtime.Misc;
using System;
using System.Linq;
using System.Text;
using Antlr4.Runtime.Misc;
using Sixty502DotNet.Runtime;

namespace Sixty502DotNet
{
/// <summary>
Expand Down Expand Up @@ -926,6 +928,7 @@ public override BlockState VisitExitBlock([NotNull] Sixty502DotNetParser.ExitBlo
var pc = Services.Output.LogicalPC;
Services.StatementListings.Add($"{GenLineListing(Services)}.{pc,-42:x4}{context.label().GetText()}");
}

return BlockState.Evaluating;
}

Expand Down Expand Up @@ -1020,32 +1023,43 @@ private string GenLineLabelOnly()
/// <returns>The generated listing as a string.</returns>
public static string GenLineListing(AssemblyServices services, string disassembly)
{
if (services.State.CurrentStatement != null &&
ListingReady(services))
if (services.State.CurrentStatement is not { } cs)
{
var lineGen = services.Output.LogicalPC - services.State.LogicalPCOnAssemble > 0;
var sb = new StringBuilder(GenLineListing(services));
if (!services.Options.NoAssembly && lineGen)
{
var byteString = services.Output.GetBytesFrom(services.State.LongLogicalPCOnAssemble).ToString(services.State.LogicalPCOnAssemble, '.');
sb.Append(byteString.PadRight(25));
}
if (!services.Options.NoDisassembly && lineGen)
{
if (sb.Length > 29)
{
sb.Append(' ');
}
sb.Append(disassembly.PadRight(18));
}
if (!services.Options.NoSource && (lineGen || services.Options.VerboseList) && services.State.CurrentStatement.blockStat() == null)
return string.Empty;
}

services.DebugInfo.Add(new DebugEntry(cs.Start.Line, services.State.LongLogicalPCOnAssemble, cs.GetSourceLine(true)));

if (!ListingReady(services))
{
return string.Empty;
}

var lineGen = services.Output.LogicalPC - services.State.LogicalPCOnAssemble > 0;
var sb = new StringBuilder(GenLineListing(services));
if (!services.Options.NoAssembly && lineGen)
{
var byteString = services.Output.GetBytesFrom(services.State.LongLogicalPCOnAssemble).ToString(services.State.LogicalPCOnAssemble, '.');
sb.Append(byteString.PadRight(25));
}

if (!services.Options.NoDisassembly && lineGen)
{
if (sb.Length > 29)
{
sb.Append(services.State.CurrentStatement.GetSourceLine(services.Options.VerboseList));
sb.Append(' ');
}
services.StatementListings.Add(sb.ToString());
return sb.ToString();

sb.Append(disassembly.PadRight(18));
}
return string.Empty;

if (!services.Options.NoSource && (lineGen || services.Options.VerboseList) && cs.blockStat() == null)
{
sb.Append(cs.GetSourceLine(services.Options.VerboseList));
}

services.StatementListings.Add(sb.ToString());
return sb.ToString();
}

/// <summary>
Expand Down
198 changes: 100 additions & 98 deletions Sixty502DotNet/src/Runtime/AssemblyServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,107 +7,109 @@

using System;
using System.Collections.Generic;
using Sixty502DotNet.Runtime;

namespace Sixty502DotNet
namespace Sixty502DotNet;

/// <summary>
/// A service for all shared assembly resources and state, such as
/// <see cref="ErrorLog"/>, <see cref="CodeOutput"/> , and
/// <see cref="SymbolManager"/> support classes.
/// </summary>
public class AssemblyServices
{
/// <summary>
/// A service for all shared assembly resources and state, such as
/// <see cref="ErrorLog"/>, <see cref="CodeOutput"/> , and
/// <see cref="SymbolManager"/> support classes.
/// Construct a new instance of the <see cref="AssemblyServices"/>
/// class.
/// </summary>
public class AssemblyServices
/// <param name="options">The parsed command-line options.</param>
public AssemblyServices(Options options)
{
/// <summary>
/// Construct a new instance of the <see cref="AssemblyServices"/>
/// class.
/// </summary>
/// <param name="options">The parsed command-line options.</param>
public AssemblyServices(Options options)
{
Options = options;
Encoding = new AsmEncoding();
Output = new CodeOutput();
Log = new ErrorLog(Options.WarningsAsErrors);
Symbols = new SymbolManager(Options.CaseSensitive);
BuiltInSymbols.Define(this);
ExpressionVisitor = new ExpressionVisitor(this);
StatementListings = new List<string>();
LabelListing = new LabelListing();
CPU = OutputFormat = string.Empty;
State = new AssemblyState();
}

/// <summary>
/// The <see cref="ErrorLog"/> object used for all error and warning
/// logging.
/// </summary>
public ErrorLog Log { get; }

/// <summary>
/// The <see cref="AsmEncoding"/> responsible for all encoding.
/// </summary>
public AsmEncoding Encoding { get; init; }

/// <summary>
/// The <see cref="ExpressionVisitor"/> responsible for visitng
/// expression parse trees and returning a resulting <see cref="IValue"/>.
/// </summary>
public ExpressionVisitor ExpressionVisitor { get; init; }

/// <summary>
/// The <see cref="CodeOutput"/> object managing all output of assembly.
/// </summary
public CodeOutput Output { get; init; }

/// <summary>
/// Gets the output format name.
/// </summary>
public string OutputFormat { get; private set; }

/// <summary>
/// Gets or sets the CPU.
/// </summary>
public string CPU { get; set; }

/// <summary>
/// Gets the <see cref="SymbolManager"/> responsible for tracking all
/// defined symbols.
/// </summary>
public SymbolManager Symbols { get; init; }

/// <summary>
/// Get the current assembly state.
/// </summary>
public AssemblyState State { get; init; }

/// <summary>
/// The <see cref="Sixty502DotNet.Options"/> object responsible for parsing
/// and enumerating all command line options.
/// </summary>
public Options Options { get; }

/// <summary>
/// Get the statement listings as a list of strings.
/// </summary>
public List<string> StatementListings { get; init; }

/// <summary>
/// Get the <see cref="LabelListing"/> object.
/// </summary>
public LabelListing LabelListing { get; init; }

/// <summary>
/// Gets the <see cref="System.StringComparer"/> on the case-sensitive
/// flag of the set <see cref="Sixty502DotNet.Options"/>.
/// </summary>
public StringComparer StringComparer
=> Options.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;

/// <summary>
/// Gets the <see cref="System.StringComparison"/> on the case-sensitive
/// flag of the set <see cref="Sixty502DotNet.Options"/>.
/// </summary>
public StringComparison StringComparison
=> Options.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
Options = options;
Encoding = new AsmEncoding();
Output = new CodeOutput();
Log = new ErrorLog(Options.WarningsAsErrors);
Symbols = new SymbolManager(Options.CaseSensitive);
BuiltInSymbols.Define(this);
ExpressionVisitor = new ExpressionVisitor(this);
StatementListings = new List<string>();
LabelListing = new LabelListing();
CPU = OutputFormat = string.Empty;
State = new AssemblyState();
}
}

/// <summary>
/// The <see cref="ErrorLog"/> object used for all error and warning
/// logging.
/// </summary>
public ErrorLog Log { get; }

/// <summary>
/// The <see cref="AsmEncoding"/> responsible for all encoding.
/// </summary>
public AsmEncoding Encoding { get; init; }

/// <summary>
/// The <see cref="ExpressionVisitor"/> responsible for visitng
/// expression parse trees and returning a resulting <see cref="IValue"/>.
/// </summary>
public ExpressionVisitor ExpressionVisitor { get; init; }

/// <summary>
/// The <see cref="CodeOutput"/> object managing all output of assembly.
/// </summary
public CodeOutput Output { get; init; }

/// <summary>
/// Gets the output format name.
/// </summary>
public string OutputFormat { get; private set; }

/// <summary>
/// Gets or sets the CPU.
/// </summary>
public string CPU { get; set; }

/// <summary>
/// Gets the <see cref="SymbolManager"/> responsible for tracking all
/// defined symbols.
/// </summary>
public SymbolManager Symbols { get; init; }

/// <summary>
/// Get the current assembly state.
/// </summary>
public AssemblyState State { get; init; }

/// <summary>
/// The <see cref="Sixty502DotNet.Options"/> object responsible for parsing
/// and enumerating all command line options.
/// </summary>
public Options Options { get; }

/// <summary>
/// Get the statement listings as a list of strings.
/// </summary>
public List<string> StatementListings { get; init; }

/// <summary>
/// Get the <see cref="LabelListing"/> object.
/// </summary>
public LabelListing LabelListing { get; init; }

/// <summary>
/// Gets the <see cref="System.StringComparer"/> on the case-sensitive
/// flag of the set <see cref="Sixty502DotNet.Options"/>.
/// </summary>
public StringComparer StringComparer
=> Options.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;

/// <summary>
/// Gets the <see cref="System.StringComparison"/> on the case-sensitive
/// flag of the set <see cref="Sixty502DotNet.Options"/>.
/// </summary>
public StringComparison StringComparison
=> Options.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;

public List<DebugEntry> DebugInfo = new();
}
3 changes: 3 additions & 0 deletions Sixty502DotNet/src/Runtime/DebugEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Sixty502DotNet.Runtime;

public record DebugEntry(int Line, int ProgramCounter, string LineText);
9 changes: 5 additions & 4 deletions Sixty502DotNet/src/Z80Assembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ namespace Sixty502DotNet;
public class Z80Assembler
{
private CodeGenVisitor _codeGenVisitor;
private readonly AssemblyServices _services = new(Options.FromArgs(new[] { "-c", "z80" }));
private readonly AssemblyServices _services = new(Options.FromArgs(new[] { "-c", "z80", "--list", "fake.s" }));

public Result<byte[]> Assemble(string input)
public Result<AssemblyData> Assemble(string input)
{
var parsedSource = ParseSource(input);
if (_services.Log.HasErrors)
{
_services.Log.DumpErrors(_services.Options.NoHighlighting);
return Result.Failure<byte[]>(_services.Log.ToString());
return Result.Failure<AssemblyData>(_services.Log.ToString());
}

var passNeeded = true;
Expand All @@ -35,7 +35,8 @@ public Result<byte[]> Assemble(string input)
passNeeded = _services.State.PassNeeded;
}

return _services.Output.GetCompilation().ToArray();
var debugInfo = _services.DebugInfo.Distinct().OrderBy(x => x.Line);
return new AssemblyData(_services.Output.GetCompilation().ToArray(), debugInfo);
}

private void DoPass(IParseTree parse)
Expand Down

0 comments on commit bb49876

Please sign in to comment.